diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..7a1331f Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/build_versioned_mkdocs.yml b/.github/workflows/build_versioned_mkdocs.yml new file mode 100644 index 0000000..96b9394 --- /dev/null +++ b/.github/workflows/build_versioned_mkdocs.yml @@ -0,0 +1,46 @@ +name: MkDocs Build +on: + push: + branches: + - jtb + workflow_dispatch: +permissions: + contents: write + pages: write + id-token: write + +jobs: + build_mkdocs: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: 3.9 + - run: pip install mkdocs mkdocs-material mkdocstrings "mkdocstrings[python]" + - run: mkdocs gh-deploy --force + + deploy_mkdocs: + needs: build_mkdocs + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: gh-pages + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 2d279a0..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# .readthedocs.yaml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the OS, Python version and other tools you might need -build: - os: ubuntu-22.04 - tools: - python: "3.12" - # You can also specify other tool versions: - # nodejs: "19" - # rust: "1.64" - # golang: "1.19" - -# Build documentation in the "docs/" directory with Sphinx -sphinx: - configuration: docs/conf.py -# Optionally build your docs in additional formats such as PDF and ePub -# formats: -# - pdf -# - epub - -# Optional but recommended, declare the Python requirements required -# to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -python: - install: - - requirements: docs/requirements.txt diff --git a/README.md b/README.md index 02108f9..cd65db4 100644 --- a/README.md +++ b/README.md @@ -1,658 +1,24 @@ -# XSpect +# XSpecT -## Resources +## Installation -https://bitbucket.org/lclsbioin/xspect/downloads/Spectroscopy_Code.pptx +Clone the repository: -Post processing module notes: - https://docs.google.com/document/d/1ZHLMMExlau5z6l_AUbby3waSofyO33W6LL-rySKJC1I/edit -## License -Copyright 2024 XSpecT Team + ```bash + git clone https://github.com/lg345/XSpecT.git + ``` -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -## Installation - -*In progress* - -## Getting Started - -*In progress* - -## Post Processing -Draft by Ryan Ribson - -### FXN: irfconv -- Performs numerical convolution between heaviside exponential function and gaussian IRF (gaussian area normalized to 1) -- Inputs: - - x = x axis (time) - - k = list or array of exponential rate constants (can have more than one) - - center = center position of gaussian IRF (will define "time zero") - - sigma = standard deviation of gaussian IRF - - amp = amplitude for scaling the function (optional, default will be 1) -- Outputs: - - The function gives a 2D array back: the first dimension will be that of the provided x grid (time), the second dimension will be \# of k values given; i.e. a matrix where each column is a convolved monoexponential function with rate constant k\[i\] -- Function accepts an array of x axis values along which it returns the convolved function - - From these values, it generates a new array of linearly spaced x values along which to perform the convolution - - I increase the maximum time over which the convolution is evaluated beyond the original x axis to avoid numerical truncation effects (sometimes seen at the end of the convolution) - - I also increase the number of steps along the x axis by a factor of 10: if the time binning is too coarse (say you have time bins of 200 fs, but are trying to describe a gaussian IRF with sigma = 100 fs), the normalization will not work correctly (even just testing numerical integration of the normalized gaussian function, you approach an area value of 1 only once you have sufficient sampling along the x axis) - - From a data perspective, having time binning sufficiently larger than your IRF width kind of nullifies the effect of the IRF in the first place, but from a fitting/analysis perspective, fitting functions might steer the IRF sigma to values smaller than your time steps in the absence of constraints, which would cause unexpected swings in amplitudes that can affect your fit; ensuring excess sampling in the convolution grid should help to avoid this problem and maintain reasonable normalization -- The heaviside exponential function and normalized gaussian are evaluated over the convolution x grid and then numerically convolved using scipy.signal.fftconvolve -- If two vectors of length m and length n are convolved, the resulting convolution vector will be of length m+n-1, so we need to effectively extract the portion of the convolution containing our signal and then interpolate that portion back onto our original time axis - - Want to test how this works with non-linearly binned input time (x axis) values -- Example code to visualize effect of varying sigma on shape of function near time zero: - -```python -import numpy as np -import matplotlib.pyplot as plt -import colorcet.plotting as cc -import XSpect.XSpect_PostProcessing -``` - -```python -pana = XSpect.XSpect_PostProcessing.post_analysis() -k1 = [0.08] ## rate constant (ps^-1^) -irf_t0 = 0 ## time zero -t = np.linspace(-20, 100, 1921) ## time vector (in ps) -irfsig_list = [0, 0.1, 0.2, 0.5, 0.8] ## IRF sigma values -kpl = plt.cm.get_cmap('cet_bmy', len(irfsig_list)*2) -bmy = kpl(range(10)) -``` - -```python -for i in range(len(irfsig_list)): - plt.plot(t, pana.**irfconv**(t, k1, irf_t0, irfsig_list[i]), color = bmy[i,:]) -``` - -```python -plt.xlim([-3, 5]) -plt.legend(irfsig_list, title = r'$IRF sigma$') -plt.xlabel('Time (ps)') -plt.ylabel('Amplitude') -plt.title('Gaussian IRF Convolution (Numerical)') -plt.show() -``` -![](media/image10.png) - -### FXN: irfconv_ana -- Evaluates analytical expression for the convolution of exponential and gaussian functions -- Inputs: - - x = array of x values (time) - - k = list or array of exponential rate constants - - center = time zero - - sigma = gaussian IRF standard deviation - - amp = amplitudes for scaling -- Outputs: - - The function gives a 2D array back: the first dimension will be that of the provided x grid (time), the second dimension will be # of k values given; i.e. a matrix where each column is a convolved monoexponential function with rate constant k[i] -- Analytical expression: -![equation](https://latex.codecogs.com/svg.image?$$I(t)=\frac{A}{2}\cdot&space;exp\lbrack-k(t-t_{0})\rbrack\cdot&space;exp\lbrack&space;0.5\ast{(k\sigma)}^{2}\rbrack\cdot(1+erf\lbrack\frac{t-t_{0}-k\sigma^{2}}{\sigma}\rbrack)) - - A = amplitude - - k = exponential rate constant - - t~0 = "time zero" - - 𝝈 = gaussian IRF standard deviation/width parameter - - erf is the error function -- I have the code return the heaviside exponential function if 𝝈 = 0 is provided with time zero shifted to value given by "center" -- Example code to visualize effect of varying sigma on shape of function near time zero (and check that it is consistent with numerical results): - -```python -import numpy as np -import matplotlib.pyplot as plt -import colorcet.plotting as cc -import XSpect.XSpect_PostProcessing -``` - -```python -k1 = [0.08] -irf_t0 = 0 -t = np.linspace(-20, 100, 1921) -irfsig_list = [0, 0.1, 0.2, 0.5, 0.8] -kpl = plt.cm.get_cmap('cet_bmy', len(irfsig_list)*2) -bmy = kpl(range(10)) -pana = XSpect.XSpect_PostProcessing.post_analysis() -``` - -```python -for i in range(len(irfsig_list)): - plt.plot(t, pana.**irfconv_ana**(t, k1, irf_t0, irfsig_list[i]), color = bmy[i,:]) -``` - -```python -plt.xlim([-3, 5]) -plt.legend(irfsig_list, title = r'$IRF sigma$') -plt.xlabel('Time (ps)') -plt.ylabel('Amplitude') -plt.title('Gaussian IRF Convolution (Analytical)') -plt.show() -``` -![](media/image9.png) - -### **FXN: kmatsolver** -- Function solves a system of rate equations purely composed of unimolecular (first-order) steps; in these cases, the analytical solution exists and can be found using the eigenvectors/values of the rate constant (k) matrix; building block of global/target kinetic analysis fitting -- Inputs: - - kmatrix = function mapping k values to an np.array modeling the desired k matrix - - x = array of x values (time) - - k = list or array of exponential rate constants - - X0 = initial conditions (which species has what population at t=0, given as a list or array) - - center = time zero - - sigma = gaussian IRF standard deviation - - irf_option = 'numerical' (default), 'analytical', or 'none' determines which function to evaluate exponentials with (irfconv, irfconv_ana, and expfunc_heaviside respectively) - - printopt = True (default), prints k matrix (when calling this function iteratively, say in a fitting algorithm, I set this to False) -- Outputs: - - The function gives a 2D array back: each ith column represents the concentration profile of the ith species in the kinetic model evaluated over the given x values -- Example code solving a sequential system of kinetic equations: -$$ -(A \dashrightarrow B \dashrightarrow C \dashrightarrow decays) -$$ - -```python -import numpy as np -import matplotlib.pyplot as plt -import colorcet.plotting as cc -import XSpect.XSpect_PostProcessing -``` - -```python -pana = XSpect.XSpect_PostProcessing.post_analysis() -``` - -```python -k1 = [0.08, 0.01, 0.002] ## rate constants -irf_c = 0 ## time zero -irf_sig = 0.2 ## IRF std dev (ps) -``` - -```python -t = np.linspace(-20, 800, 1641) # time (ps) -``` - -```python -X0 = [1, 0, 0] # initial conditions, at time zero, 1st species is at full population and 2nd and 3rd have no population -``` - -```python -Km = lambda x: np.array([(-x[0], 0, 0), (x[0], -x[1], 0), (0, x[1], -x[2])]) ## K matrix as a lambda fxn, in kmatsolver will take k list as an input and generate the appropriate K matrix -``` - -```python -C = pana.**kmatsolver**(Km, t, k1, X0, irf_c, irf_sig) -plt.plot(t, C, linewidth = 2) -plt.xlabel('Time (ps)') -plt.ylabel('Amplitude') -plt.xlim([min(t), max(t)]) -plt.legend(['Species 1', 'Species 2', 'Species 3']) -plt.title('kmatsolver Results') -plt.show() -``` -![](media/image11.png) - -- ### **FXN: svdplot** - - Performs singular value decomposition (SVD) and plots user defined number of left singular vectors, singular values, right singular vectors - - Inputs: - - xval = x axis - - yval = y axis - - data = 2D data set to perform SVD on - - ncomp = number of components to plot - - Outputs: - - Plots ncomp left and right singular values and scree plot of - singular values - - Example code using svdplot, we will simulate a dataset using two spectra with a sequential kinetic model and added noise then plot the first 4 components of the SVD (we should only expect 2 with significant singular values and structure in the singular vectors) - -```python -import matplotlib.pyplot as plt -import colorcet.plotting as cc -import XSpect.XSpect_PostProcessing -``` - -```python -pana = XSpect.XSpect_PostProcessing.post_analysis() -``` - -```python -## let\'s simulate some data first to do SVD on -print(\'Simulating dataset:\') -``` - -```python -## first we\'ll simulate our C(t) matrix (C_sim) -t = np.linspace(-20, 500, 4000) -k_sim = [0.08, 0.01] -irf_t0 = 0.5 -irf_sig = 0.15 -X0_sim = [1, 0] -Km = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) -C_sim = pana.kmatsolver(Km, t, k_sim, X0_sim, irf_t0, irf_sig) -``` +Ensure that you have the necessary dependencies installed on your system. -```python -## then will simulate some gaussian "spectra" (spec_sim) -energy = np.linspace(7030, 7080, (7080-7030)*5) -spec_x = [7050, 7055] -spec_sigma = [5, 6, 4] -spec_amp = [0.8, 0.9, 0.85] -spec_sim = np.empty([len(energy), len(spec_x)]) -for i in range(len(spec_x)): -spec_sim[:,i] = spec_amp[i]*pana.gaussfunc(energy, spec_x[i], -spec_sigma[i]) -``` +## Getting Started -```python -## calculate full data matrix (A_sim) and add noise -A = C_sim@np.transpose(spec_sim) -noise = np.random.normal(loc = 0, scale = 0.2, size = (len(t), -len(energy))) -A_sim = A + noise -``` +Check out our [documentation](https://lg345.github.io/XSpecT/index.html) page for source code details and examples to get started. -```python -## Plot simulated data (C_sim(t), spec_sim(energy), A_sim(t, energy)) -plotmax = np.max(np.abs(A_sim)) -contlevels = np.linspace(-plotmax, plotmax, 100) -fig, ax = plt.subplots(ncols = 3, nrows = 1, figsize = (15, 5)) -``` +## License +Copyright 2025 XSpecT Team -```python -p1 = ax[0].plot(t, C_sim, linewidth = 2) -ax[0].set_xlabel('Time') -ax[0].set_ylabel('Amplitude') -ax[0].set_xlim([min(t), max(t)]) -ax[0].set_title('C_sim') -``` - -```python -p2 = ax[1].plot(energy, spec_sim, linewidth = 2) -ax[1].set_xlabel('Energy') -ax[1].set_ylabel('Amplitude') -ax[1].set_xlim([min(energy), max(energy)]) -ax[1].set_title('spec_sim') -``` - -```python -p3 = ax[2].contourf(energy, t, A_sim, contlevels, cmap = 'RdBu') -ax[2].set_xlabel('Energy') -ax[2].set_ylabel('Time') -ax[2].set_title('A_sim') -cb = fig.colorbar(p3, ax = ax[2]) -``` - -```python -plt.show() -print('Running svdplot:') -ncomp = 4 -pana.**svdplot**(t, energy, A_sim, ncomp) -``` -![](media/image13.png) - -- ### **FXN: svdreconstruct** - - Performs SVD reconstruction (filtering) on data using the first n components of the SVD - - Inputs: - - data = data set to perform SVD and SVD filtering on - - ncomp = number of components to use to reconstruct the dataset from the SVD - - Outputs: - - A_filt = 2D array containing the SVD reconstructed dataset - - Example code using SVD reconstruction for filtering out noise: - - -```python -import numpy as np -import matplotlib.pyplot as plt -import colorcet.plotting as cc -import XSpect.XSpect_PostProcessing -``` - -```python -pana = XSpect.XSpect_PostProcessing.post_analysis() -``` - -```python -## let's simulate some data first to do SVD on -print('Simulating dataset:') -``` - -```python -## first we'll simulate our C(t) matrix (C_sim) -t = np.linspace(-20, 500, 4000) -k_sim = [0.08, 0.01] -irf_t0 = 0.5 -irf_sig = 0.15 -X0_sim = [1, 0] -Km = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) -C_sim = pana.kmatsolver(Km, t, k_sim, X0_sim, irf_t0, irf_sig) -``` - -```python -## then will simulate some gaussian "spectra" (spec_sim) -energy = np.linspace(7030, 7080, (7080-7030)\*5) -spec_x = [7050, 7055] -spec_sigma = [5, 6, 4] -spec_amp = [0.8, 0.9, 0.85] -spec_sim = np.empty([len(energy), len(spec_x)]) -for i in range(len(spec_x)): -spec_sim[:,i] = spec_amp[i]*pana.gaussfunc(energy, spec_x[i], -spec_sigma[i]) -``` - -```python -## calculate full data matrix (A_sim) and add noise -A = C_sim@np.transpose(spec_sim) -noise = np.random.normal(loc = 0, scale = 0.2, size = (len(t), -len(energy))) -A_sim = A + noise -``` - -```python -## Plot simulated data (C_sim(t), spec_sim(energy), A_sim(t, energy)) -plotmax = np.max(np.abs(A_sim)) -contlevels = np.linspace(-plotmax, plotmax, 100) -fig, ax = plt.subplots(ncols = 3, nrows = 1, figsize = (15, 5)) -``` - -```python -p1 = ax[0].plot(t, C_sim, linewidth = 2) -ax[0].set_xlabel('Time') -ax[0].set_ylabel('Amplitude') -ax[0].set_xlim([min(t), max(t)]) -ax[0].set_title('C_sim') -``` - -```python -p2 = ax[1].plot(energy, spec_sim, linewidth = 2) -ax[1].set_xlabel('Energy') -ax[1].set_ylabel('Amplitude') -ax[1].set_xlim([min(energy), max(energy)]) -ax[1].set_title('spec_sim') -``` - -```python -p3 = ax[2].contourf(energy, t, A_sim, contlevels, cmap = 'RdBu') -ax[2].set_xlabel('Energy') -ax[2].set_ylabel('Time') -ax[2].set_title('A_sim') -cb = fig.colorbar(p3, ax = ax[2]) -plt.show() -``` - -```python -print('Running svdreconstruct:') -ncomp = 2 -A_filt = pana.**svdreconstruct**(A_sim, ncomp) -``` - -```python -fig, ax = plt.subplots(ncols = 2, nrows = 1, figsize = (15,5)) -p1 = ax[0].contourf(energy, t, A_filt, contlevels, cmap = 'RdBu') -ax[0].set_xlabel('Energy') -ax[0].set_ylabel('Time') -ax[0].set_title('Data Reconstructed') -cb = fig.colorbar(p1, ax = ax[0]) -``` - -```python -index_cut = np.argmin(np.abs(energy - 7050)) -ax[1].plot(A_sim[:, index_cut]) -ax[1].plot(A_filt[:, index_cut]) -ax[1].set_xlabel('Time') -ax[1].set_ylabel('Amplitude') -ax[1].set_title('Data vs SVD Filtered Data') -ax[1].legend(['A_sim', 'A_filt']) -plt.show() -``` -![](media/image12.png) - - -- ### **FXN: varproj** - - Performs variable projection using a kinetic model and a provided data set; building block of global/target kinetic analysis fitting - - Inputs: - - kmatrix = function mapping k values to an np.array modeling the desired k matrix - - x = array of x values (time) - - k = list or array of exponential rate constants - - X0 = initial conditions (which species has what population at t=0, given as a list or array) - - center = time zero - - sigma = gaussian IRF standard deviation - - data = experimental data on which you are performing variable projection - - Outputs: - - C = matrix of concentration column vectors - - E = matrix of decay/evolution/species associated spectra (projected from data) - - SimA = simulated data matrix using kinetic model and variable projection - - We really only need SimA to calculate residuals for a fitting algorithm, but getting C and E as outputs is useful once we want to visualize the final results of the fit - - Workflow: - - In variable projection method we assume a bilinear relationship between time and energy variables (they are separable): -$$ -A(t,\lambda)\ = \ C(t)E^{T}(\lambda) -$$ - - Matrix form of Beer-Lambert Law where $C(t)$ is a matrix of column vectors containing time-dependent concentrations of a given species and $E^{T}(\lambda)$ is a matrix of row vectors containing energy-dependent spectra of a given species - - **Note:** the way that I have defined this means that by default the expected dimensions of the data matrix A will be 1st dimension (rows) = time and 2nd dimension (columns) = energy - - We generally choose one set of variables to parameterize for fitting (often time, i.e. kinetic modelling). The other set of variables are taken as linearly conditional on the parameterized set - - The spectral vectors in $E$ are projected from the experimental data using the calculated concentration vectors $C$; $pinv()$ is the Moore-Penrose pseudo inverse -$$ -pinv(C)A\ = \ pinv(C)CE^{T} -$$ -$$ -E^{T} = \ pinv(C)A -$$ - - The simulated data set can then be constructed as follows: -$$ -A_{\text{sim}} = C_{\text{sim}}E^{T} = C_{\text{sim}}pinv(C_{\text{sim}})A_{\exp} -$$ - - This can then be used to calculate residuals in a global/target kinetic analysis objective function -- ### **FXN: targetobjective** - - Target analysis objective function, takes parameter vector as - primary input, calculates simulated data using parameters - defined in theta, and returns residuals - - Inputs: - - theta = vector containing parameters to be optimized in fit - - x = array of x values (time) - - kmatrix = function mapping k values to an np.array modeling - the desired k matrix - - x = array of x values (time) - - X0 = initial conditions (which species has what population - at t=0, given as a list or array) - - theta_parser = dictionary of parameters generated from - function "parse_theta", tells us how to read theta and map - to function inputs - - data = experimental data to be fit - - Outputs: - - A 1D array containing residuals of the experimental data - minus data simulated via the function "varproj" - - Workflow: - - The variables in the vector theta are sorted into a new - dictionary based on the structure of theta_parser - - These variables are then passed to the varproj function to - calculate simulated data matrix given the kinetic model - supplied - - The residuals are calculated and then sorted into a 1D array -- ### **FXN: targetanalysis_run** - - Running target kinetic analysis using the - scipy.optimize.least_squares function - - Inputs: - - data = experimental data to fit - - x = array of x values (time) - - kmatrix = function mapping k values to an np.array modeling - the desired k matrix - - k_in = list or array of initial guess exponential rate - constants - - center_in = initial guess time zero - - sigma_in = initial guess gaussian IRF standard deviation - - X0_in = initial conditions (which species has what - population at t=0, given as a list or array) for kinetic - model - - y = array of y values, energy (optional) - - bounds_dict = dictionary of parameter constraints (optional, - see below) - - Outputs: - - res_lsq = object containing the fit results from - scipy.optimize.least_squares run - - C_fit = concentration matrix calculated using fitted - parameters - - E_fit = decay/evolution/species associated spectra - calculated using fitted parameters - - Additional outputs include: - - Printing dictionary containing fitted parameters - - Printing final cost function from least squares at - optimized position (taken from res_lsq.cost, seems - to be printing residual sum of squares divided - by 2) - - Plotting concentration profiles and - decay/evolution/species associated spectra of each - component - - Workflow: - - The support functions parse_theta are used to package k_in, - center_in, and sigma_in into a dictionary format that can - be used to map the variables to (construct_theta) and from - (read_theta) the parameter vector (theta) that will be - provided to scipy.optimize.least_squares - - Parameter constraints are set if an appropriate bound - dictionary is provided, otherwise default constraints are - applied - - The fitting algorithm is run using the target objective - function, the generated parameter vector (theta), and the - other inputs necessary for target objective function - - The fit parameters are read out into a dictionary that is - printed - - The final cost of the least squares function is printed - - The fitted concentration profiles and - decay/evolution/species associated spectra are plotted - - Some quirks: - - In the exponential functions, I take the absolute value of - the given k values to ensure exponential decay, i.e. - - fxn = a\*exp(-kt) - - For exponential decay, k will always be positive - - I think this is the most likely case we will encounter - in chemical kinetics, if we need a more general - approach we can change this - - This means that for fitting, there are symmetries in the - error surface around zero for the rate constant - parameters - - Using Levenberg-Marquardt or trust region reflective - implementations in scipy.optimize.least_squares, - this usually doesn't pose too much of a problem in - the sense that the correct value of rate constant - is usually attained, but not the necessarily the - sign - - The Levenberg-Marquardt implementation in - scipy.optimize.least_squares does not support parameter - constraints, in order to include this, I use the default - trust region reflective method - - Users can supply a "bound dictionary" to define the - lower and upper bounds for parameters; I've - arbitrarily had this take the form below: -- **Example of a bound dictionary (bd) that targetanalysis_run can accept; the overarching dictionary contains two lower level dictionaries, one for lower bounds ('lb') and one for upper bounds ('ub'); the lower/upper bounds for every parameter is then given in list form under the appropriate key - -bd = {'lb': {'k': [0, 0], 'center': [-10], 'sigma': [0]}, -'ub': {'k': [np.inf, np.inf], 'center': [10], 'sigma': -[1]}} - -- Given the considerations above, I provide a set of default bounds - - For every rate constant guess provided, a set of [0 to np.inf] bounds are given, indicating that the k will always be positive (and does away with the symmetry around zero problem) - - For irf center (t0) parameter, the default is unconstrained [-np.inf to np.inf] - - For irf sigma (linewidth) parameter, the default is constrained [0 to np.inf] -- Currently these are the only two options for targetanalysis_run - - the default constraints and user supplied constraints, can add - option for fully unconstrained though this could lead to some - issues -- Currently the only way to "fix" parameters in the fit would be to - use the bound dictionary format provided above and set the lower - and upper bound for a given parameter to be equal to the desired - "fixed" value, may be sufficient, will look into options -- I do not have an implementation yet for calculating standard errors - from the fit (though the output of scipy.optimize.least_squares - returns a jacobian, which can be used to estimate the Hessian and - by extension the covariance matrix of the parameters)\... will - work on -- Example where a 2D data set in time and energy is simulated and then the target analysis fitting protocol is run (using a bound dictionary): - -```python -import numpy as np -import matplotlib.pyplot as plt -import colorcet.plotting as cc -import XSpect.XSpect_PostProcessing -pana = XSpect.XSpect_PostProcessing.post_analysis() -``` - -```python -## let's simulate some data to test the fitting on first -print('Simulating dataset:') -``` - -```python -## first we'll simulate our C(t) matrix (C_sim) -t = np.linspace(-20, 500, 4000) -k_sim = [0.08, 0.01] -irf_t0 = 0.5 -irf_sig = 0.15 -X0_sim = [1, 0] -Km = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) -C_sim = pana.kmatsolver(Km, t, k_sim, X0_sim, irf_t0, irf_sig) -``` - -```python -## then will simulate some gaussian "spectra" (spec_sim) -energy = np.linspace(7030, 7080, (7080-7030)*5) -spec_x = [7050, 7055] -spec_sigma = [5, 6, 4] -spec_amp = [0.8, 0.9, 0.85] -spec_sim = np.empty([len(energy), len(spec_x)]) -for i in range(len(spec_x)): -spec_sim[:,i] = spec_amp[i]*pana.gaussfunc(energy, spec_x[i], -spec_sigma[i]) -``` - -```python -## calculate full data matrix (A_sim) and add noise -A = C_sim@np.transpose(spec_sim) -noise = np.random.normal(loc = 0, scale = 0.2, size = (len(t), -len(energy))) -A_sim = A + noise -``` - -```python -## Plot simulated data (C_sim(t), spec_sim(energy), A_sim(t, energy)) -plotmax = np.max(np.abs(A_sim)) -contlevels = np.linspace(-plotmax, plotmax, 100) -fig, ax = plt.subplots(ncols = 3, nrows = 1, figsize = (15, 7)) -p1 = ax[0].plot(t, C_sim, linewidth = 2) -ax[0].set_xlabel('Time') -ax[0].set_ylabel('Amplitude') -ax[0].set_xlim([min(t), max(t)]) -ax[0].set_title('C_sim') -p2 = ax[1].plot(energy, spec_sim, linewidth = 2) -ax[1].set_xlabel('Energy') -ax[1].set_ylabel('Amplitude') -ax[1].set_xlim([min(energy), max(energy)]) -ax[1].set_title('spec_sim') -p3 = ax[2].contourf(energy, t, A_sim, contlevels, cmap = 'RdBu') -ax[2].set_xlabel('Energy') -ax[2].set_ylabel('Time') -ax[2].set_title('A_sim') -cb = fig.colorbar(p3, ax = ax[2]) -plt.show() -``` - -```python -## now that we have a data set to fit, we'll set up a guess -## this includes starting points for fit parameters (k and irf) -## as well as a kinetic model to use (the k matrix defined in K_guess) -``` - -```python -## and run the fitting procedure -print('Running fitting protocol:') -k_guess = [0.2, 0.001] -irf_t0_guess = [0] -irf_sigma_guess = [1] -K_guess = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) -X0_guess = [1, 0] -bd = {'lb': {'k': [0, 0], 'center': [-10], 'sigma': [0]}, 'ub': {'k': [np.inf, np.inf], 'center': [10], -'sigma': [1]}} -fit, C_fit, spec_fit = pana.**targetanalysis_run**(A_sim, t, K_guess, -k_guess, irf_t0_guess, irf_sigma_guess, X0_guess, y = energy, -bounds_dict = bd) -``` +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -![](media/image14.png) +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -![](media/image8.png) - +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/XSpect/XSpect_Analysis.py b/XSpect/XSpect_Analysis.py index bb00fd1..67fdf75 100644 --- a/XSpect/XSpect_Analysis.py +++ b/XSpect/XSpect_Analysis.py @@ -12,6 +12,7 @@ import argparse from datetime import datetime import tempfile + class experiment: def __init__(self, lcls_run, hutch, experiment_id): """ @@ -19,12 +20,16 @@ def __init__(self, lcls_run, hutch, experiment_id): Parameters ---------- + lcls_run : str LCLS run identifier. The LCLS run not the scan/run. Example: 21 + hutch : str Hutch name. Example: xcs + experiment_id : str Experiment identifier. Example: xcsl1004021 + """ self.lcls_run = lcls_run self.hutch = hutch @@ -37,13 +42,16 @@ def get_experiment_directory(self): Returns ------- + str The directory of the experiment. Raises ------ + Exception If the directory cannot be found. + """ experiment_directories = [ '/sdf/data/lcls/ds/%s/%s/hdf5/smalldata', @@ -79,18 +87,24 @@ def __init__(self,spec_experiment,run,verbose=False,end_index=-1,start_index=0): Parameters ---------- + spec_experiment : spectroscopy_experiment The parent spectroscopy experiment. + run : int The run number. + verbose : bool, optional Flag for verbose output used for printing all of the status updates. These statuses are also available in the object itself. Defaults to False. + end_index : int, optional Index to stop processing data. Defaults to -1. + start_index : int, optional Index to start processing data. Defaults to 0. These indices are used for batch analysis. + """ self.spec_experiment=spec_experiment self.run_number=run @@ -114,10 +128,13 @@ def update_status(self,update): """ Updates the status log for the run and appends it to the objects status/datetime attibutes. If verbose then it prints it. + Parameters ---------- + update : str The status update message. + """ self.status.append(update) self.status_datetime.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) @@ -149,10 +166,13 @@ def load_run_keys(self, keys, friendly_names): Parameters ---------- + keys : list List of keys to load from the hdf5 file + friendly_names : list Corresponding list of friendly names for the keys. Some keys are special to the subsequent analyis e.g. epix and ipm. + """ start=time.time() with h5py.File(self.run_file, 'r') as fh: @@ -174,17 +194,23 @@ def load_run_key_delayed(self, keys, friendly_names, transpose=False, rois=None, Parameters ---------- + keys : list List of keys to load. + friendly_names : list Corresponding list of friendly names for the keys. + transpose : bool, optional Flag to transpose the loaded data. Defaults to False. + rois : list of lists, optional List of ROIs (regions of interest) as pixel ranges along one dimension (default is None). Each ROI should be in the form [start_col, end_col]. + combine : bool, optional Whether to combine ROIs into a single mask. Defaults to True. + """ start = time.time() fh = h5py.File(self.run_file, 'r') @@ -234,13 +260,17 @@ def load_sum_run_scattering(self,key,low=20,high=80): Parameters ---------- + key : str The key to sum the scattering data from. + low : int Low index for summing + high: int high index for summing These indices should be chosen over the water ring or some scattering of interest. + """ with h5py.File(self.run_file, 'r') as fh: setattr(self, 'scattering', np.nansum(np.nansum(fh[key][:,:,low:high],axis=1),axis=1)) @@ -259,8 +289,10 @@ def purge_all_keys(self,keys_to_keep): Parameters ---------- + keys_to_keep : list List of keys to retain. + """ new_dict = {attr: value for attr, value in self.__dict__.items() if attr in keys_to_keep} @@ -279,10 +311,13 @@ def bin_uniques(self,run,key): Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + key : str The key for which unique values are to be binned. + """ vals = getattr(run,key) bins = np.unique(vals) @@ -302,18 +337,23 @@ def bin_uniques(self,run,key): def filter_shots(self, run,shot_mask_key, filter_key='ipm', threshold=1.0E4): """ Filters shots based on a given threshold. + For example, if we filter: xray,ipm,1E4 then X-ray shots will be filtered out if the ipm is below 1E4. Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + shot_mask_key : str The key corresponding to the shot mask. An example being [xray,simultaneous,laser] for all x-ray shots + filter_key : str, optional The key corresponding to the filter data (default is 'ipm'). + threshold : float, optional The threshold value for filtering (default is 1.0E4). - So if we filter: xray,ipm,1E4 then X-ray shots will be filtered out if the ipm is below 1E4. + """ shot_mask=getattr(run,shot_mask_key) count_before=np.sum(shot_mask) @@ -331,12 +371,16 @@ def filter_nan(self, run,shot_mask_key, filter_key='ipm'): Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + shot_mask_key : str The key corresponding to the shot mask. + filter_key : str, optional The key corresponding to the filter data (default is 'ipm'). + """ shot_mask=getattr(run,shot_mask_key) count_before=np.sum(shot_mask) @@ -355,17 +399,22 @@ def filter_detector_adu(self,run,detector,adu_threshold=3.0): Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + detector : str The key corresponding to the detector data. + adu_threshold : float or list of float, optional The ADU threshold for filtering. Can be a single value or a range (default is 3.0). - + Returns ------- + np.ndarray The filtered detector data. + """ detector_images=getattr(run,detector) if isinstance(adu_threshold,list): @@ -388,10 +437,13 @@ def purge_keys(self,run,keys): Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + keys : list of str The list of keys to purge. + """ for detector_key in keys: setattr(run, detector_key, None) @@ -416,20 +468,28 @@ def reduce_detector_spatial(self, run, detector_key, shot_range=[0, None], rois= Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + shot_range : list, optional The range of shots to consider (default is [0, None]). + rois : list of lists, optional The list of ROIs (regions of interest) as pixel ranges (default is [[0, None]]). + reduction_function : function, optional The function to apply for reduction (default is np.sum). + purge : bool, optional Whether to purge the original detector data after reduction (default is True). + combine : bool, optional Whether to combine ROIs (default is True). + """ detector = getattr(run, detector_key) if combine: @@ -469,16 +529,22 @@ def time_binning(self,run,bins,lxt_key='lxt_ttc',fast_delay_key='encoder',tt_cor Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + bins : array-like The bins to use for time binning. + lxt_key : str, optional The key for the laser time delay data (default is 'lxt_ttc'). + fast_delay_key : str, optional The key for the fast delay data (default is 'encoder'). + tt_correction_key : str, optional The key for the time tool correction data (default is 'time_tool_correction'). + """ if lxt_key==None: run.delays = 0+ getattr(run,fast_delay_key) + getattr(run,tt_correction_key) @@ -495,12 +561,16 @@ def union_shots(self, run, detector_key, filter_keys,new_key=True): Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + filter_keys : list of str The list of filter keys to combine. + """ detector = getattr(run, detector_key) @@ -524,12 +594,16 @@ def separate_shots(self, run, detector_key, filter_keys): Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + filter_keys : list of str The list of filter keys to separate. + """ detector = getattr(run, detector_key) if isinstance(filter_keys, list): @@ -548,14 +622,19 @@ def reduce_detector_temporal(self, run, detector_key, timing_bin_key_indices,ave Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + timing_bin_key_indices : str The key corresponding to the timing bin indices. + average : bool, optional Whether to average the data within each bin (default is False). + """ detector = getattr(run, detector_key) indices = getattr(run, timing_bin_key_indices) @@ -581,20 +660,28 @@ def patch_pixels(self,run,detector_key, mode='average', patch_range=4, deg=1, p Parameters ---------- + run : spectroscopy_run The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + mode : str, optional The mode of patching ('average', 'polynomial', or 'interpolate'). + patch_range : int, optional The range around the pixel to use for patching (default is 4). + deg : int, optional The degree of the polynomial for polynomial patching (default is 1). + poly_range : int, optional The range of pixels to use for polynomial or interpolation patching (default is 6). + axis : int, optional The axis along which to apply the patching (default is 1). + """ for pixel in self.pixels_to_patch: self.patch_pixel(run,detector_key,pixel,mode,patch_range,deg,poly_range,axis=axis) @@ -604,24 +691,34 @@ def patch_pixel(self, run, detector_key, pixel, mode='average', patch_range=4, d """ EPIX detector pixel patching. TODO: extend to patch regions instead of per pixel. + Parameters ---------- + data : array_like Array of shots + pixel : integer Pixel point to be patched + mode : string Determines which mode to use for patching the pixel. Averaging works well. + patch_range : integer Pixels away from the pixel to be patched to be used for patching. Needed if multiple pixels in a row are an issue. + deg : integer Degree of polynomial if polynomial patching is used. + poly_range : integer Number of pixels to include in the polynomial or interpolation fitting + Returns ------- + float The original data with the new patch values. + """ data = getattr(run, detector_key) @@ -756,12 +853,16 @@ def normalize_xes(self,run,detector_key,pixel_range=[300,550]): Parameters ---------- + run : object The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + pixel_range : list of int, optional The pixel range to sum over for normalization (default is [300, 550]). + """ detector = getattr(run, detector_key) row_sum = np.sum(detector[:, pixel_range[0]:pixel_range[1]], axis=1) @@ -773,12 +874,16 @@ def make_energy_axis(self, run,energy_axis_length, A, R, mm_per_pixel=0.05, d=0 Parameters ---------- + A : float The detector to vH distance (mm) and can roughly float. This will affect the spectral offset. + R : float The vH crystal radii (mm) and should not float. This will affect the spectral stretch. + pixel_array : array-like Array of pixels to determine the energy of. + d : float Crystal d-spacing. To calculate, visit: spectra.tools/bin/controller.pl?body=Bragg_Angle_Calculator @@ -802,19 +907,25 @@ def reduce_det_scanvar(self, run, detector_key, scanvar_key, scanvar_bins_key): Parameters ---------- + run : object The spectroscopy run instance. + detector_key : str The key corresponding to the detector data within the run object. + scanvar_key : str The key corresponding to the scan variable indices. + scanvar_bins_key : str The key corresponding to the scan variable bins. Returns ------- + None The reduced data is stored in the `run` object with the key formatted as `{detector_key}_scanvar_reduced`. + """ detector = getattr(run, detector_key) @@ -846,10 +957,13 @@ def trim_ccm(self,run,threshold=120): Parameters ---------- + run : object The spectroscopy run instance. + threshold : int, optional The minimum number of shots required to keep a CCM value (default is 120). + """ ccm_bins=getattr(run,'ccm_bins',elist_center) @@ -864,10 +978,13 @@ def make_ccm_axis(self,run,energies): Parameters ---------- + run : object The spectroscopy run instance. + energies : array-like Array of energy values to be used for creating CCM bins. + """ elist=energies # addon = (elist[-1] - elist[-2])/2 # add on energy @@ -924,14 +1041,19 @@ def reduce_detector_ccm(self, run, detector_key, ccm_bin_key_indices, average = Parameters ---------- + run : object The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + ccm_bin_key_indices : str The key corresponding to the CCM bin indices. + average : bool, optional Whether to average the reduced data (default is False). + not_ccm : bool, optional Whether to indicate that CCM is not being used (default is False). @@ -954,14 +1076,19 @@ def reduce_detector_temporal(self, run, detector_key, timing_bin_key_indices, av Parameters ---------- + run : object The spectroscopy run instance. + detector_key : str The key corresponding to the detector data. + timing_bin_key_indices : str The key corresponding to the timing bin indices. + average : bool, optional Whether to average the reduced data (default is False). + """ detector = getattr(run, detector_key) time_bins=run.time_bins @@ -977,12 +1104,16 @@ def ccm_binning(self,run,ccm_bins_key,ccm_key='ccm'): Parameters ---------- + run : object The spectroscopy run instance. + ccm_bins_key : str The key corresponding to the CCM bins. + ccm_key : str, optional The key corresponding to the CCM data (default is 'ccm'). + """ ccm=getattr(run,ccm_key) bins=getattr(run,ccm_bins_key) diff --git a/docs/.DS_Store b/docs/.DS_Store new file mode 100644 index 0000000..7acc91b Binary files /dev/null and b/docs/.DS_Store differ diff --git a/docs/Getting_started_XAS.md b/docs/Getting_started_XAS.md new file mode 100644 index 0000000..775470f --- /dev/null +++ b/docs/Getting_started_XAS.md @@ -0,0 +1,252 @@ +## Importing Dependencies + +XSpecT relies on a number of common python packages including: + +- [h5py](https://www.h5py.org/) for reading HDF5 files +- [NumPy](https://numpy.org/) and scipy for data analysis +- [Matplotlib](https://matplotlib.org/) for visualization +- Other system related packages + +Depending on your system you may need to install the necessary +dependencies. S3DF users should have the necessary packages by default. + +``` py +import h5py +import numpy as np +import matplotlib.pyplot as plt +from scipy.ndimage import rotate +from scipy.interpolate import interp1d +from scipy.optimize import curve_fit,minimize +import multiprocessing +import os +from functools import partial +import time +import sys +import argparse +from datetime import datetime +import tempfile +``` + +## Importing XSPecT Modules + +XSpecT has several main modules for function to control various aspects +of the analysis, visualization, diagnostics and overall processing. + +``` py +sys.path.insert(0, './XSpecT/') +import XSpect.XSpect_Analysis +import XSpect.XSpect_Controller +import XSpect.XSpect_Visualization +import XSpect.XSpect_PostProcessing +import XSpect.XSpect_Diagnostics +``` + +## XAS Analysis Example + +### Setting up experiment parameters + +Initializing the `spectroscopy_experiment` class and setting the relevant +experiment information`lslc_run`, `hutch`, and `experiment_id` +parameters. + +``` py +xas_experiment = XSpect.XSpect_Analysis.spectroscopy_experiment(lcls_run=22, hutch='xcs', experiment_id='xcsl1030422') +``` + +These values will be used to obtain the directory for the data which is +stored in `experiment_directory`: + +``` py +xas_experiment.experiment_directory +``` + + '/sdf/data/lcls/ds/xcs/xcsl1030422/hdf5/smalldata' + +### XASBatchAnalysis Class + +Instantiating the `XASBatchAnalysis` class which allows you to set +attributes relevant to the analysis such as the HDF5 group keys for the +various datasets, filter thresholds, and timing/energy parameters. The +class also contain an analysis pipeline method, which controls the +sequence of analysis operations. + +``` py +xas=XSpect.XSpect_Controller.XASBatchAnalysis() +``` + +#### Setting keys and aliases + +The keys, which specify the data to read from the HDF5 file, are defined +as a list of strings. For pump-probe XAS measurements this typically +includes: + +- The monochromator energy and set values (epics/ccm_E, epicsUser/ccm_E\_setpoint) +- TT correction values and amplitude (tt/ttCorr, tt/AMPL) +- Timing stage values (epics/lxt_ttc) +- Emission CCD detecotr ROI sum values (epix_2/ROI_0\_sum) +- Normalization channel (ipm4/sum). + +Their "friendly" names serve as an easier to remember alias for the keys and are also defined as a list of strings with the same ordering as the keys. +These lists are passed to `set_key_aliases` which creates the key aliases. + +``` py +keys=['epics/ccm_E', 'epicsUser/ccm_E_setpoint', 'tt/ttCorr', 'epics/lxt_ttc', 'enc/lasDelay', 'ipm4/sum', 'tt/AMPL', 'epix_2/ROI_0_sum'] +names=['ccm', 'ccm_E_setpoint', 'time_tool_correction', 'lxt_ttc', 'encoder', 'ipm', 'time_tool_ampl', 'epix'] +xas.set_key_aliases(keys,names) +``` + +#### Adding filters + +Filters are set using `add_filter` which takes requires the parameters +\'shot_type\' (e.g. xray, simultaneous), \'filter_key\' (i.e. which +dataset to apply the filter to), and the filter threshold. + +``` py +xas.add_filter('xray','ipm',500.0) +xas.add_filter('simultaneous','ipm',500.0) +xas.add_filter('simultaneous','time_tool_ampl',0.01) +``` + +#### Setting runs + +Multiple runs (files) can be analyzed and combined into a single data set using the `run_parser` method. +Specify the runs as a list of strings or as a single string with space separated run numbers. +Ranges can be specified using numbers separated by a "-". + +``` py +xas.run_parser(['240-243 245-254']) +``` + +#### Setting timing parameters + +Delay timing range and number of points is set in picoseconds. + +``` py +xas.mintime = -0.5 +xas.maxtime = 2.0 +xas.numpoints = 25 +``` + +#### Normalization option + +Normalization is set by default (False) to use an IPM sum dataset. +Alternatively, the scattering liquid ring signal can be used: + +``` py +xas.scattering = True +``` + +### Running Analysis Loop + +With the necessary parameters set the analysis procedure can be +initiatilized. Here you pass the experiment attributes from +`xas_experiment`. For details of the step by step analysis processes set +`verbose= True ` (False is the default). + +``` py +xas.primary_analysis_loop(xas_experiment, verbose=True) +``` + + Obtained shot properties + HDF5 import of keys completed. Time: 0.02 seconds + Mask: xray has been filtered on ipm by minimum threshold: 500.000 + Shots removed: 2645 + Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000 + Shots removed: 1904 + Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010 + Shots removed: 100 + Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser + Shots (12182) separated for detector epix on filters: xray and laser into epix_xray_laser + Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser + Shots (12182) separated for detector ipm on filters: xray and laser into ipm_xray_laser + Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser + Shots (12182) separated for detector ccm on filters: xray and laser into ccm_xray_laser + Generated timing bins from -0.500000 to 2.000000 in 25 steps. + Generated ccm bins from 7.105000 to 7.156500 in 54 steps. + Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser + Shots (12182) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser + Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser + Shots (12182) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser + Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned + Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned + Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned + Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned + Obtained shot properties + HDF5 import of keys completed. Time: 0.03 seconds + ... + +#### Exploring Analyzed Runs + +The data for each run is stored in `analyzed_runs` list. + +``` py +xas.analyzed_runs +``` + + [, + , + , + , + , + , + , + , + , + , + , + , + , + ] + +We can check the data shape for the laser-off shots first analyzed run, +which has the dimensions of 25 time bins by 54 energy bins. + +``` py +print("Data shape:", xas.analyzed_runs[0].epix_xray_not_laser_time_energy_binned.shape) +``` + + Data shape: (25, 54) + +Since the laser the laser is off, we can average across all time bins for the epix and normalization channels. + +``` py +y = np.average(xas.analyzed_runs[0].epix_xray_not_laser_time_energy_binned, axis = 0) +norm = np.average(xas.analyzed_runs[0].ipm_xray_not_laser_time_energy_binned, axis =0) +``` + +#### Plotting Laser-off Spectrum + +Then the laser off spectrum can be plotted versus the monochromator energies. + +``` py +plt.plot(xas.analyzed_runs[0].ccm_energies, y/norm, label="Laser-off") +plt.xlabel("Energy (eV)") +plt.ylabel("Normalized XAS") +plt.legend() +``` + +![](media/laseroff.png) + +#### Plotting 2D Spectra + +The 2D (time versus energy) data can be summed and plotted using the XSpecT visualation module. +First, from visualization the `XASVisualization` object is instantiated. +Then, using the `combine_spectra` method and passing the `xas` data object and the necessary data keys the data is processed. + +``` py +v=XSpect.XSpect_Visualization.XASVisualization() +v.combine_spectra(xas_analysis=xas, + xas_laser_key='epix_simultaneous_laser_time_energy_binned', + xas_key='epix_xray_not_laser_time_energy_binned', + norm_laser_key='ipm_simultaneous_laser_time_energy_binned', + norm_key='ipm_xray_not_laser_time_energy_binned') +``` + +Finally, the 2D spectrum can be plotted, setting vmin and vmax colorbar +parameters as needed. + +``` py +v.plot_2d_difference_spectrum(xas, vmin=-0.3, vmax=0.3) +``` + +![](media/2Dplot.png) diff --git a/docs/Getting_started_XES.md b/docs/Getting_started_XES.md new file mode 100644 index 0000000..9e95c02 --- /dev/null +++ b/docs/Getting_started_XES.md @@ -0,0 +1,120 @@ +``` py +import h5py +import numpy as np +import matplotlib.pyplot as plt +from scipy.ndimage import rotate +from scipy.interpolate import interp1d +from scipy.optimize import curve_fit,minimize +import multiprocessing +import os +from functools import partial +import time +import sys +import argparse +from datetime import datetime +import tempfile +import XSpect.XSpect_Analysis +import XSpect.XSpect_Controller +import XSpect.XSpect_Visualization +``` + +## Static XES Spectra + +``` py +xes_experiment = XSpect.XSpect_Analysis.spectroscopy_experiment(hutch='mfx',experiment_id='mfxx1013623',lcls_run=23) +xes=XSpect.XSpect_Controller.XESBatchAnalysisRotation() +xes.key_epix=['epix_1/ROI_0_area'] +#xes.set_key_aliases(keys,names) +xes.import_roi=[[523,535]] +xes.rois=[[0,12]] +xes.adu_cutoff=3.0 +xes.angle=0 +xes.transpose=True +xes.run_parser(['37']) +start=time.time() +xes.primary_analysis_parallel_range(4,xes_experiment,method=xes.primary_analysis_static,increment=2000,verbose=False) +end=time.time() +v=XSpect.XSpect_Visualization.XESVisualization() +v.combine_static_spectra(xes_analysis=xes,xes_key='epix_ROI_1') +plt.plot(v.summed_xes) +``` + + Processing: 100%|██████████| 17/17 [00:43<00:00, 2.55s/Shot_Batch] + + [] + +![](media/77615fc07aea654fbf2201cddc214b1008b2608d.png) + +## 2D Time-resolved XES Spectra +``` py +xes_experiment = XSpect.XSpect_Analysis.spectroscopy_experiment(hutch='mfx',experiment_id='mfxl1027922',lcls_run=22) +xes=XSpect.XSpect_Controller.XESBatchAnalysisRotation() +keys=['tt/ttCorr','epics/lxt', 'enc/lasDelay' , 'ipm4/sum','tt/AMPL'] +names=['time_tool_correction','lxt_ttc' ,'encoder','ipm', 'time_tool_ampl'] +#Here we define the epix detector keys separately as they are imported separately to avoid OOM +xes.key_epix=[r'epix_2/ROI_0_area'] +xes.friendly_name_epix=['epix'] +## +xes.set_key_aliases(keys,names) +#xes.end_index=5000 +xes.mintime=-0.9 +xes.maxtime=0.9 +xes.numpoints=40 +xes.time_bins=np.linspace(xes.mintime,xes.maxtime,xes.numpoints) +xes.rois=[[0,50]] +xes.adu_cutoff=3.0 +xes.angle=90 +xes.lxt_key=None +xes.transpose=True +#xes.add_filter('xray','ipm4',1.0E3) +#xes.add_filter('simultaneous','ipm4',1.0E3) +xes.add_filter('simultaneous','time_tool_ampl',0.05) +xes.run_parser(['44-46']) +``` + +``` py +start=time.time() +xes.primary_analysis_parallel_range(8,xes_experiment,increment=1000,verbose=False) +end=time.time() +``` + + Processing: 100%|██████████| 30/30 [02:25<00:00, 4.85s/Shot_Batch] + +``` py +xes.status +``` + + ['Setting key aliases.', + 'Adding filter: Shot Type=simultaneous, Filter Key=time_tool_ampl, Threshold=0.05', + 'Parsing run array.', + 'Starting parallel analysis with shot ranges.', + 'Parsing run shots.', + 'Run shots parsed.', + 'Breaking into shot ranges with increment 1000.', + 'Shot ranges broken.', + 'Parallel analysis with shot ranges completed.', + 'Parallel analysis completed.', + 'Total time: 145.59 seconds.', + 'Parallel time (processing): 145.59 seconds.', + 'Time per batch (on average): 4.85 seconds.', + 'Time per core (on average): 18.20 seconds.', + 'Batches per core (on average): 3.75.', + 'Read bytes: 13.14 MB.', + 'Write bytes: 4055.88 MB.', + 'Memory used: 52.87 MB.'] + +``` py +v=XSpect.XSpect_Visualization.XESVisualization() +v.combine_spectra(xes_analysis=xes,xes_key='epix_xray_not_laser_time_binned_ROI_1',xes_laser_key='epix_simultaneous_laser_time_binned_ROI_1') +v.vmin=-0.006 +v.vmax=0.004 +v.plot_2d_difference_spectrum(xes) +plt.xlim(-0.8,0.8) +``` + + (-0.8, 0.8) + +![](media/f22364f0824ce6092523ca08e6a277dd8abc3f46.png) + +``` py +``` diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d4bb2cb..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/Overview.md b/docs/Overview.md new file mode 100644 index 0000000..62ac8b4 --- /dev/null +++ b/docs/Overview.md @@ -0,0 +1,9 @@ +# XSpecT + +XSpecT follows the principles of [Model-View-Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) in its design. It is written in [python](https://www.python.org/) following the object-oriented programming paradigm. It is designed for analyzing X-ray absorption and emission spectroscopy data at X-ray free electron lasers and is currently in use at the [LCLS](https://lcls.slac.stanford.edu/). + +The XSPecT code is comprised of several main modules designed to handle the analysis, visualization and control processes. The controller handles i/o, analysis pipelines and parallelization. The analysis module performs the data manipulations (e.g. filtering, sorting and mathematical operations). The visualization module provides methods to easily generate 2D difference maps and other plots of interest. + +The over process is depicted in the following figure: +![](media/diagram.png) + diff --git a/docs/Post_Processing.md b/docs/Post_Processing.md new file mode 100644 index 0000000..b75fa15 --- /dev/null +++ b/docs/Post_Processing.md @@ -0,0 +1,630 @@ +### FXN: irfconv +- Performs numerical convolution between heaviside exponential function and gaussian IRF (gaussian area normalized to 1) +- Inputs: + - x = x axis (time) + - k = list or array of exponential rate constants (can have more than one) + - center = center position of gaussian IRF (will define "time zero") + - sigma = standard deviation of gaussian IRF + - amp = amplitude for scaling the function (optional, default will be 1) +- Outputs: + - The function gives a 2D array back: the first dimension will be that of the provided x grid (time), the second dimension will be \# of k values given; i.e. a matrix where each column is a convolved monoexponential function with rate constant k\[i\] +- Function accepts an array of x axis values along which it returns the convolved function + - From these values, it generates a new array of linearly spaced x values along which to perform the convolution + - I increase the maximum time over which the convolution is evaluated beyond the original x axis to avoid numerical truncation effects (sometimes seen at the end of the convolution) + - I also increase the number of steps along the x axis by a factor of 10: if the time binning is too coarse (say you have time bins of 200 fs, but are trying to describe a gaussian IRF with sigma = 100 fs), the normalization will not work correctly (even just testing numerical integration of the normalized gaussian function, you approach an area value of 1 only once you have sufficient sampling along the x axis) + - From a data perspective, having time binning sufficiently larger than your IRF width kind of nullifies the effect of the IRF in the first place, but from a fitting/analysis perspective, fitting functions might steer the IRF sigma to values smaller than your time steps in the absence of constraints, which would cause unexpected swings in amplitudes that can affect your fit; ensuring excess sampling in the convolution grid should help to avoid this problem and maintain reasonable normalization +- The heaviside exponential function and normalized gaussian are evaluated over the convolution x grid and then numerically convolved using scipy.signal.fftconvolve +- If two vectors of length m and length n are convolved, the resulting convolution vector will be of length m+n-1, so we need to effectively extract the portion of the convolution containing our signal and then interpolate that portion back onto our original time axis + - Want to test how this works with non-linearly binned input time (x axis) values +- Example code to visualize effect of varying sigma on shape of function near time zero: + +```python +import numpy as np +import matplotlib.pyplot as plt +import colorcet.plotting as cc +import XSpect.XSpect_PostProcessing +``` + +```python +pana = XSpect.XSpect_PostProcessing.post_analysis() +k1 = [0.08] ## rate constant (ps^-1^) +irf_t0 = 0 ## time zero +t = np.linspace(-20, 100, 1921) ## time vector (in ps) +irfsig_list = [0, 0.1, 0.2, 0.5, 0.8] ## IRF sigma values +kpl = plt.cm.get_cmap('cet_bmy', len(irfsig_list)*2) +bmy = kpl(range(10)) +``` + +```python +for i in range(len(irfsig_list)): + plt.plot(t, pana.**irfconv**(t, k1, irf_t0, irfsig_list[i]), color = bmy[i,:]) +``` + +```python +plt.xlim([-3, 5]) +plt.legend(irfsig_list, title = r'$IRF sigma$') +plt.xlabel('Time (ps)') +plt.ylabel('Amplitude') +plt.title('Gaussian IRF Convolution (Numerical)') +plt.show() +``` +![](media/image10.png) + +### FXN: irfconv_ana +- Evaluates analytical expression for the convolution of exponential and gaussian functions +- Inputs: + - x = array of x values (time) + - k = list or array of exponential rate constants + - center = time zero + - sigma = gaussian IRF standard deviation + - amp = amplitudes for scaling +- Outputs: + - The function gives a 2D array back: the first dimension will be that of the provided x grid (time), the second dimension will be # of k values given; i.e. a matrix where each column is a convolved monoexponential function with rate constant k[i] +- Analytical expression: +![equation](https://latex.codecogs.com/svg.image?$$I(t)=\frac{A}{2}\cdot&space;exp\lbrack-k(t-t_{0})\rbrack\cdot&space;exp\lbrack&space;0.5\ast{(k\sigma)}^{2}\rbrack\cdot(1+erf\lbrack\frac{t-t_{0}-k\sigma^{2}}{\sigma}\rbrack)) + - A = amplitude + - k = exponential rate constant + - t~0 = "time zero" + - 𝝈 = gaussian IRF standard deviation/width parameter + - erf is the error function +- I have the code return the heaviside exponential function if 𝝈 = 0 is provided with time zero shifted to value given by "center" +- Example code to visualize effect of varying sigma on shape of function near time zero (and check that it is consistent with numerical results): + +```python +import numpy as np +import matplotlib.pyplot as plt +import colorcet.plotting as cc +import XSpect.XSpect_PostProcessing +``` + +```python +k1 = [0.08] +irf_t0 = 0 +t = np.linspace(-20, 100, 1921) +irfsig_list = [0, 0.1, 0.2, 0.5, 0.8] +kpl = plt.cm.get_cmap('cet_bmy', len(irfsig_list)*2) +bmy = kpl(range(10)) +pana = XSpect.XSpect_PostProcessing.post_analysis() +``` + +```python +for i in range(len(irfsig_list)): + plt.plot(t, pana.**irfconv_ana**(t, k1, irf_t0, irfsig_list[i]), color = bmy[i,:]) +``` + +```python +plt.xlim([-3, 5]) +plt.legend(irfsig_list, title = r'$IRF sigma$') +plt.xlabel('Time (ps)') +plt.ylabel('Amplitude') +plt.title('Gaussian IRF Convolution (Analytical)') +plt.show() +``` +![](media/image9.png) + +### **FXN: kmatsolver** +- Function solves a system of rate equations purely composed of unimolecular (first-order) steps; in these cases, the analytical solution exists and can be found using the eigenvectors/values of the rate constant (k) matrix; building block of global/target kinetic analysis fitting +- Inputs: + - kmatrix = function mapping k values to an np.array modeling the desired k matrix + - x = array of x values (time) + - k = list or array of exponential rate constants + - X0 = initial conditions (which species has what population at t=0, given as a list or array) + - center = time zero + - sigma = gaussian IRF standard deviation + - irf_option = 'numerical' (default), 'analytical', or 'none' determines which function to evaluate exponentials with (irfconv, irfconv_ana, and expfunc_heaviside respectively) + - printopt = True (default), prints k matrix (when calling this function iteratively, say in a fitting algorithm, I set this to False) +- Outputs: + - The function gives a 2D array back: each ith column represents the concentration profile of the ith species in the kinetic model evaluated over the given x values +- Example code solving a sequential system of kinetic equations: +$$ +(A \dashrightarrow B \dashrightarrow C \dashrightarrow decays) +$$ + +```python +import numpy as np +import matplotlib.pyplot as plt +import colorcet.plotting as cc +import XSpect.XSpect_PostProcessing +``` + +```python +pana = XSpect.XSpect_PostProcessing.post_analysis() +``` + +```python +k1 = [0.08, 0.01, 0.002] ## rate constants +irf_c = 0 ## time zero +irf_sig = 0.2 ## IRF std dev (ps) +``` + +```python +t = np.linspace(-20, 800, 1641) # time (ps) +``` + +```python +X0 = [1, 0, 0] # initial conditions, at time zero, 1st species is at full population and 2nd and 3rd have no population +``` + +```python +Km = lambda x: np.array([(-x[0], 0, 0), (x[0], -x[1], 0), (0, x[1], -x[2])]) ## K matrix as a lambda fxn, in kmatsolver will take k list as an input and generate the appropriate K matrix +``` + +```python +C = pana.**kmatsolver**(Km, t, k1, X0, irf_c, irf_sig) +plt.plot(t, C, linewidth = 2) +plt.xlabel('Time (ps)') +plt.ylabel('Amplitude') +plt.xlim([min(t), max(t)]) +plt.legend(['Species 1', 'Species 2', 'Species 3']) +plt.title('kmatsolver Results') +plt.show() +``` +![](media/image11.png) + +- ### **FXN: svdplot** + - Performs singular value decomposition (SVD) and plots user defined number of left singular vectors, singular values, right singular vectors + - Inputs: + - xval = x axis + - yval = y axis + - data = 2D data set to perform SVD on + - ncomp = number of components to plot + - Outputs: + - Plots ncomp left and right singular values and scree plot of + singular values + - Example code using svdplot, we will simulate a dataset using two spectra with a sequential kinetic model and added noise then plot the first 4 components of the SVD (we should only expect 2 with significant singular values and structure in the singular vectors) + +```python +import matplotlib.pyplot as plt +import colorcet.plotting as cc +import XSpect.XSpect_PostProcessing +``` + +```python +pana = XSpect.XSpect_PostProcessing.post_analysis() +``` + +```python +## let\'s simulate some data first to do SVD on +print(\'Simulating dataset:\') +``` + +```python +## first we\'ll simulate our C(t) matrix (C_sim) +t = np.linspace(-20, 500, 4000) +k_sim = [0.08, 0.01] +irf_t0 = 0.5 +irf_sig = 0.15 +X0_sim = [1, 0] +Km = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) +C_sim = pana.kmatsolver(Km, t, k_sim, X0_sim, irf_t0, irf_sig) +``` + +```python +## then will simulate some gaussian "spectra" (spec_sim) +energy = np.linspace(7030, 7080, (7080-7030)*5) +spec_x = [7050, 7055] +spec_sigma = [5, 6, 4] +spec_amp = [0.8, 0.9, 0.85] +spec_sim = np.empty([len(energy), len(spec_x)]) +for i in range(len(spec_x)): +spec_sim[:,i] = spec_amp[i]*pana.gaussfunc(energy, spec_x[i], +spec_sigma[i]) +``` + +```python +## calculate full data matrix (A_sim) and add noise +A = C_sim@np.transpose(spec_sim) +noise = np.random.normal(loc = 0, scale = 0.2, size = (len(t), +len(energy))) +A_sim = A + noise +``` + +```python +## Plot simulated data (C_sim(t), spec_sim(energy), A_sim(t, energy)) +plotmax = np.max(np.abs(A_sim)) +contlevels = np.linspace(-plotmax, plotmax, 100) +fig, ax = plt.subplots(ncols = 3, nrows = 1, figsize = (15, 5)) +``` + +```python +p1 = ax[0].plot(t, C_sim, linewidth = 2) +ax[0].set_xlabel('Time') +ax[0].set_ylabel('Amplitude') +ax[0].set_xlim([min(t), max(t)]) +ax[0].set_title('C_sim') +``` + +```python +p2 = ax[1].plot(energy, spec_sim, linewidth = 2) +ax[1].set_xlabel('Energy') +ax[1].set_ylabel('Amplitude') +ax[1].set_xlim([min(energy), max(energy)]) +ax[1].set_title('spec_sim') +``` + +```python +p3 = ax[2].contourf(energy, t, A_sim, contlevels, cmap = 'RdBu') +ax[2].set_xlabel('Energy') +ax[2].set_ylabel('Time') +ax[2].set_title('A_sim') +cb = fig.colorbar(p3, ax = ax[2]) +``` + +```python +plt.show() +print('Running svdplot:') +ncomp = 4 +pana.**svdplot**(t, energy, A_sim, ncomp) +``` +![](media/image13.png) + +- ### **FXN: svdreconstruct** + - Performs SVD reconstruction (filtering) on data using the first n components of the SVD + - Inputs: + - data = data set to perform SVD and SVD filtering on + - ncomp = number of components to use to reconstruct the dataset from the SVD + - Outputs: + - A_filt = 2D array containing the SVD reconstructed dataset + - Example code using SVD reconstruction for filtering out noise: + + +```python +import numpy as np +import matplotlib.pyplot as plt +import colorcet.plotting as cc +import XSpect.XSpect_PostProcessing +``` + +```python +pana = XSpect.XSpect_PostProcessing.post_analysis() +``` + +```python +## let's simulate some data first to do SVD on +print('Simulating dataset:') +``` + +```python +## first we'll simulate our C(t) matrix (C_sim) +t = np.linspace(-20, 500, 4000) +k_sim = [0.08, 0.01] +irf_t0 = 0.5 +irf_sig = 0.15 +X0_sim = [1, 0] +Km = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) +C_sim = pana.kmatsolver(Km, t, k_sim, X0_sim, irf_t0, irf_sig) +``` + +```python +## then will simulate some gaussian "spectra" (spec_sim) +energy = np.linspace(7030, 7080, (7080-7030)\*5) +spec_x = [7050, 7055] +spec_sigma = [5, 6, 4] +spec_amp = [0.8, 0.9, 0.85] +spec_sim = np.empty([len(energy), len(spec_x)]) +for i in range(len(spec_x)): +spec_sim[:,i] = spec_amp[i]*pana.gaussfunc(energy, spec_x[i], +spec_sigma[i]) +``` + +```python +## calculate full data matrix (A_sim) and add noise +A = C_sim@np.transpose(spec_sim) +noise = np.random.normal(loc = 0, scale = 0.2, size = (len(t), +len(energy))) +A_sim = A + noise +``` + +```python +## Plot simulated data (C_sim(t), spec_sim(energy), A_sim(t, energy)) +plotmax = np.max(np.abs(A_sim)) +contlevels = np.linspace(-plotmax, plotmax, 100) +fig, ax = plt.subplots(ncols = 3, nrows = 1, figsize = (15, 5)) +``` + +```python +p1 = ax[0].plot(t, C_sim, linewidth = 2) +ax[0].set_xlabel('Time') +ax[0].set_ylabel('Amplitude') +ax[0].set_xlim([min(t), max(t)]) +ax[0].set_title('C_sim') +``` + +```python +p2 = ax[1].plot(energy, spec_sim, linewidth = 2) +ax[1].set_xlabel('Energy') +ax[1].set_ylabel('Amplitude') +ax[1].set_xlim([min(energy), max(energy)]) +ax[1].set_title('spec_sim') +``` + +```python +p3 = ax[2].contourf(energy, t, A_sim, contlevels, cmap = 'RdBu') +ax[2].set_xlabel('Energy') +ax[2].set_ylabel('Time') +ax[2].set_title('A_sim') +cb = fig.colorbar(p3, ax = ax[2]) +plt.show() +``` + +```python +print('Running svdreconstruct:') +ncomp = 2 +A_filt = pana.**svdreconstruct**(A_sim, ncomp) +``` + +```python +fig, ax = plt.subplots(ncols = 2, nrows = 1, figsize = (15,5)) +p1 = ax[0].contourf(energy, t, A_filt, contlevels, cmap = 'RdBu') +ax[0].set_xlabel('Energy') +ax[0].set_ylabel('Time') +ax[0].set_title('Data Reconstructed') +cb = fig.colorbar(p1, ax = ax[0]) +``` + +```python +index_cut = np.argmin(np.abs(energy - 7050)) +ax[1].plot(A_sim[:, index_cut]) +ax[1].plot(A_filt[:, index_cut]) +ax[1].set_xlabel('Time') +ax[1].set_ylabel('Amplitude') +ax[1].set_title('Data vs SVD Filtered Data') +ax[1].legend(['A_sim', 'A_filt']) +plt.show() +``` +![](media/image12.png) + + +- ### **FXN: varproj** + - Performs variable projection using a kinetic model and a provided data set; building block of global/target kinetic analysis fitting + - Inputs: + - kmatrix = function mapping k values to an np.array modeling the desired k matrix + - x = array of x values (time) + - k = list or array of exponential rate constants + - X0 = initial conditions (which species has what population at t=0, given as a list or array) + - center = time zero + - sigma = gaussian IRF standard deviation + - data = experimental data on which you are performing variable projection + - Outputs: + - C = matrix of concentration column vectors + - E = matrix of decay/evolution/species associated spectra (projected from data) + - SimA = simulated data matrix using kinetic model and variable projection + - We really only need SimA to calculate residuals for a fitting algorithm, but getting C and E as outputs is useful once we want to visualize the final results of the fit + - Workflow: + - In variable projection method we assume a bilinear relationship between time and energy variables (they are separable): +$$ +A(t,\lambda)\ = \ C(t)E^{T}(\lambda) +$$ + - Matrix form of Beer-Lambert Law where $C(t)$ is a matrix of column vectors containing time-dependent concentrations of a given species and $E^{T}(\lambda)$ is a matrix of row vectors containing energy-dependent spectra of a given species + - **Note:** the way that I have defined this means that by default the expected dimensions of the data matrix A will be 1st dimension (rows) = time and 2nd dimension (columns) = energy + - We generally choose one set of variables to parameterize for fitting (often time, i.e. kinetic modelling). The other set of variables are taken as linearly conditional on the parameterized set + - The spectral vectors in $E$ are projected from the experimental data using the calculated concentration vectors $C$; $pinv()$ is the Moore-Penrose pseudo inverse +$$ +pinv(C)A\ = \ pinv(C)CE^{T} +$$ +$$ +E^{T} = \ pinv(C)A +$$ + - The simulated data set can then be constructed as follows: +$$ +A_{\text{sim}} = C_{\text{sim}}E^{T} = C_{\text{sim}}pinv(C_{\text{sim}})A_{\exp} +$$ + - This can then be used to calculate residuals in a global/target kinetic analysis objective function +- ### **FXN: targetobjective** + - Target analysis objective function, takes parameter vector as + primary input, calculates simulated data using parameters + defined in theta, and returns residuals + - Inputs: + - theta = vector containing parameters to be optimized in fit + - x = array of x values (time) + - kmatrix = function mapping k values to an np.array modeling + the desired k matrix + - x = array of x values (time) + - X0 = initial conditions (which species has what population + at t=0, given as a list or array) + - theta_parser = dictionary of parameters generated from + function "parse_theta", tells us how to read theta and map + to function inputs + - data = experimental data to be fit + - Outputs: + - A 1D array containing residuals of the experimental data + minus data simulated via the function "varproj" + - Workflow: + - The variables in the vector theta are sorted into a new + dictionary based on the structure of theta_parser + - These variables are then passed to the varproj function to + calculate simulated data matrix given the kinetic model + supplied + - The residuals are calculated and then sorted into a 1D array +- ### **FXN: targetanalysis_run** + - Running target kinetic analysis using the + scipy.optimize.least_squares function + - Inputs: + - data = experimental data to fit + - x = array of x values (time) + - kmatrix = function mapping k values to an np.array modeling + the desired k matrix + - k_in = list or array of initial guess exponential rate + constants + - center_in = initial guess time zero + - sigma_in = initial guess gaussian IRF standard deviation + - X0_in = initial conditions (which species has what + population at t=0, given as a list or array) for kinetic + model + - y = array of y values, energy (optional) + - bounds_dict = dictionary of parameter constraints (optional, + see below) + - Outputs: + - res_lsq = object containing the fit results from + scipy.optimize.least_squares run + - C_fit = concentration matrix calculated using fitted + parameters + - E_fit = decay/evolution/species associated spectra + calculated using fitted parameters + - Additional outputs include: + - Printing dictionary containing fitted parameters + - Printing final cost function from least squares at + optimized position (taken from res_lsq.cost, seems + to be printing residual sum of squares divided + by 2) + - Plotting concentration profiles and + decay/evolution/species associated spectra of each + component + - Workflow: + - The support functions parse_theta are used to package k_in, + center_in, and sigma_in into a dictionary format that can + be used to map the variables to (construct_theta) and from + (read_theta) the parameter vector (theta) that will be + provided to scipy.optimize.least_squares + - Parameter constraints are set if an appropriate bound + dictionary is provided, otherwise default constraints are + applied + - The fitting algorithm is run using the target objective + function, the generated parameter vector (theta), and the + other inputs necessary for target objective function + - The fit parameters are read out into a dictionary that is + printed + - The final cost of the least squares function is printed + - The fitted concentration profiles and + decay/evolution/species associated spectra are plotted + - Some quirks: + - In the exponential functions, I take the absolute value of + the given k values to ensure exponential decay, i.e. + - fxn = a\*exp(-kt) + - For exponential decay, k will always be positive + - I think this is the most likely case we will encounter + in chemical kinetics, if we need a more general + approach we can change this + - This means that for fitting, there are symmetries in the + error surface around zero for the rate constant + parameters + - Using Levenberg-Marquardt or trust region reflective + implementations in scipy.optimize.least_squares, + this usually doesn't pose too much of a problem in + the sense that the correct value of rate constant + is usually attained, but not the necessarily the + sign + - The Levenberg-Marquardt implementation in + scipy.optimize.least_squares does not support parameter + constraints, in order to include this, I use the default + trust region reflective method + - Users can supply a "bound dictionary" to define the + lower and upper bounds for parameters; I've + arbitrarily had this take the form below: +- **Example of a bound dictionary (bd) that targetanalysis_run can accept; the overarching dictionary contains two lower level dictionaries, one for lower bounds ('lb') and one for upper bounds ('ub'); the lower/upper bounds for every parameter is then given in list form under the appropriate key + +bd = {'lb': {'k': [0, 0], 'center': [-10], 'sigma': [0]}, +'ub': {'k': [np.inf, np.inf], 'center': [10], 'sigma': +[1]}} + +- Given the considerations above, I provide a set of default bounds + - For every rate constant guess provided, a set of [0 to np.inf] bounds are given, indicating that the k will always be positive (and does away with the symmetry around zero problem) + - For irf center (t0) parameter, the default is unconstrained [-np.inf to np.inf] + - For irf sigma (linewidth) parameter, the default is constrained [0 to np.inf] +- Currently these are the only two options for targetanalysis_run - + the default constraints and user supplied constraints, can add + option for fully unconstrained though this could lead to some + issues +- Currently the only way to "fix" parameters in the fit would be to + use the bound dictionary format provided above and set the lower + and upper bound for a given parameter to be equal to the desired + "fixed" value, may be sufficient, will look into options +- I do not have an implementation yet for calculating standard errors + from the fit (though the output of scipy.optimize.least_squares + returns a jacobian, which can be used to estimate the Hessian and + by extension the covariance matrix of the parameters)\... will + work on +- Example where a 2D data set in time and energy is simulated and then the target analysis fitting protocol is run (using a bound dictionary): + +```python +import numpy as np +import matplotlib.pyplot as plt +import colorcet.plotting as cc +import XSpect.XSpect_PostProcessing +pana = XSpect.XSpect_PostProcessing.post_analysis() +``` + +```python +## let's simulate some data to test the fitting on first +print('Simulating dataset:') +``` + +```python +## first we'll simulate our C(t) matrix (C_sim) +t = np.linspace(-20, 500, 4000) +k_sim = [0.08, 0.01] +irf_t0 = 0.5 +irf_sig = 0.15 +X0_sim = [1, 0] +Km = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) +C_sim = pana.kmatsolver(Km, t, k_sim, X0_sim, irf_t0, irf_sig) +``` + +```python +## then will simulate some gaussian "spectra" (spec_sim) +energy = np.linspace(7030, 7080, (7080-7030)*5) +spec_x = [7050, 7055] +spec_sigma = [5, 6, 4] +spec_amp = [0.8, 0.9, 0.85] +spec_sim = np.empty([len(energy), len(spec_x)]) +for i in range(len(spec_x)): +spec_sim[:,i] = spec_amp[i]*pana.gaussfunc(energy, spec_x[i], +spec_sigma[i]) +``` + +```python +## calculate full data matrix (A_sim) and add noise +A = C_sim@np.transpose(spec_sim) +noise = np.random.normal(loc = 0, scale = 0.2, size = (len(t), +len(energy))) +A_sim = A + noise +``` + +```python +## Plot simulated data (C_sim(t), spec_sim(energy), A_sim(t, energy)) +plotmax = np.max(np.abs(A_sim)) +contlevels = np.linspace(-plotmax, plotmax, 100) +fig, ax = plt.subplots(ncols = 3, nrows = 1, figsize = (15, 7)) +p1 = ax[0].plot(t, C_sim, linewidth = 2) +ax[0].set_xlabel('Time') +ax[0].set_ylabel('Amplitude') +ax[0].set_xlim([min(t), max(t)]) +ax[0].set_title('C_sim') +p2 = ax[1].plot(energy, spec_sim, linewidth = 2) +ax[1].set_xlabel('Energy') +ax[1].set_ylabel('Amplitude') +ax[1].set_xlim([min(energy), max(energy)]) +ax[1].set_title('spec_sim') +p3 = ax[2].contourf(energy, t, A_sim, contlevels, cmap = 'RdBu') +ax[2].set_xlabel('Energy') +ax[2].set_ylabel('Time') +ax[2].set_title('A_sim') +cb = fig.colorbar(p3, ax = ax[2]) +plt.show() +``` + +```python +## now that we have a data set to fit, we'll set up a guess +## this includes starting points for fit parameters (k and irf) +## as well as a kinetic model to use (the k matrix defined in K_guess) +``` + +```python +## and run the fitting procedure +print('Running fitting protocol:') +k_guess = [0.2, 0.001] +irf_t0_guess = [0] +irf_sigma_guess = [1] +K_guess = lambda x:np.array([(-x[0], 0), (x[0], -x[1])]) +X0_guess = [1, 0] +bd = {'lb': {'k': [0, 0], 'center': [-10], 'sigma': [0]}, 'ub': {'k': [np.inf, np.inf], 'center': [10], +'sigma': [1]}} +fit, C_fit, spec_fit = pana.**targetanalysis_run**(A_sim, t, K_guess, +k_guess, irf_t0_guess, irf_sigma_guess, X0_guess, y = energy, +bounds_dict = bd) +``` + +![](media/image14.png) + +![](media/image8.png) + diff --git a/docs/XSpect.rst b/docs/XSpect.rst deleted file mode 100644 index 31daa22..0000000 --- a/docs/XSpect.rst +++ /dev/null @@ -1,50 +0,0 @@ -XSpect package -============== - -XSpect.XSpect\_Analysis module ------------------------------- - -.. automodule:: XSpect.XSpect_Analysis - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_Controller module --------------------------------- - -.. automodule:: XSpect.XSpect_Controller - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_Diagnostics module ---------------------------------- - -.. automodule:: XSpect.XSpect_Diagnostics - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_PostProcessing module ------------------------------------- - -.. automodule:: XSpect.XSpect_PostProcessing - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_Visualization module ------------------------------------ - -.. automodule:: XSpect.XSpect_Visualization - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: XSpect - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/XSpect_Analysis.md b/docs/XSpect_Analysis.md new file mode 100644 index 0000000..f6f3d3f --- /dev/null +++ b/docs/XSpect_Analysis.md @@ -0,0 +1 @@ +::: XSpect.XSpect_Analysis diff --git a/docs/XSpect_Controller.md b/docs/XSpect_Controller.md new file mode 100644 index 0000000..c29d3aa --- /dev/null +++ b/docs/XSpect_Controller.md @@ -0,0 +1 @@ +::: XSpect.XSpect_Controller diff --git a/docs/XSpect_Diagnostics.md b/docs/XSpect_Diagnostics.md new file mode 100644 index 0000000..33590b4 --- /dev/null +++ b/docs/XSpect_Diagnostics.md @@ -0,0 +1 @@ +::: XSpect.XSpect_Diagnostics diff --git a/docs/XSpect_PostProcessing.md b/docs/XSpect_PostProcessing.md new file mode 100644 index 0000000..613a926 --- /dev/null +++ b/docs/XSpect_PostProcessing.md @@ -0,0 +1 @@ +::: XSpect.XSpect_PostProcessing diff --git a/docs/XSpect_Visualization.md b/docs/XSpect_Visualization.md new file mode 100644 index 0000000..998cd5a --- /dev/null +++ b/docs/XSpect_Visualization.md @@ -0,0 +1 @@ +::: XSpect.XSpect_Visualization diff --git a/docs/_build/doctrees/XSpect.doctree b/docs/_build/doctrees/XSpect.doctree deleted file mode 100644 index 83fdfe4..0000000 Binary files a/docs/_build/doctrees/XSpect.doctree and /dev/null differ diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle deleted file mode 100644 index 080fcce..0000000 Binary files a/docs/_build/doctrees/environment.pickle and /dev/null differ diff --git a/docs/_build/doctrees/index.doctree b/docs/_build/doctrees/index.doctree deleted file mode 100644 index fa508bb..0000000 Binary files a/docs/_build/doctrees/index.doctree and /dev/null differ diff --git a/docs/_build/doctrees/modules.doctree b/docs/_build/doctrees/modules.doctree deleted file mode 100644 index 24b550b..0000000 Binary files a/docs/_build/doctrees/modules.doctree and /dev/null differ diff --git a/docs/_build/html/XSpect.html b/docs/_build/html/XSpect.html deleted file mode 100644 index 4e0648f..0000000 --- a/docs/_build/html/XSpect.html +++ /dev/null @@ -1,1234 +0,0 @@ - - - - - - - XSpect package — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

XSpect package

-
-

Submodules

-
-
-

XSpect.XSpect_Analysis module

-
-
-class XSpect.XSpect_Analysis.SpectroscopyAnalysis[source]
-

Bases: object

-

A class to perform analysis on spectroscopy data.

-
-
-bin_uniques(run, key)[source]
-

Bins unique values for a given key within a run.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
keystr

The key for which unique values are to be binned.

-
-
-
-
- -
-
-filter_detector_adu(run, detector, adu_threshold=3.0)[source]
-

Filters is a misnomer compared to the other filter functions. -This sets detector pixel values below a threshold to 0. -Specifically, to remove 0-photon noise from detectors.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
detectorstr

The key corresponding to the detector data.

-
-
adu_thresholdfloat or list of float, optional

The ADU threshold for filtering. Can be a single value or a range (default is 3.0).

-
-
-
-
-

Returns

-
-
np.ndarray

The filtered detector data.

-
-
-
-
- -
-
-filter_nan(run, shot_mask_key, filter_key='ipm')[source]
-

A specific filtering implementation for Nans due to various DAQ issues. -Filters out shots with NaN values in the specified filter.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
shot_mask_keystr

The key corresponding to the shot mask.

-
-
filter_keystr, optional

The key corresponding to the filter data (default is ‘ipm’).

-
-
-
-
- -
-
-filter_shots(run, shot_mask_key, filter_key='ipm', threshold=10000.0)[source]
-

Filters shots based on a given threshold.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
shot_mask_keystr

The key corresponding to the shot mask. An example being [xray,simultaneous,laser] for all x-ray shots

-
-
filter_keystr, optional

The key corresponding to the filter data (default is ‘ipm’).

-
-
thresholdfloat, optional

The threshold value for filtering (default is 1.0E4).

-
-
-

So if we filter: xray,ipm,1E4 then X-ray shots will be filtered out if the ipm is below 1E4.

-
-
- -
-
-patch_pixel(run, detector_key, pixel, mode='average', patch_range=4, deg=1, poly_range=6, axis=1)[source]
-

EPIX detector pixel patching. -TODO: extend to patch regions instead of per pixel. -Parameters -———- -data : array_like

-
-

Array of shots

-
-
-
pixelinteger

Pixel point to be patched

-
-
modestring

Determined which mode to use for patching the pixel. Averaging works well.

-
-
patch_rangeinteger

pixels away from the pixel to be patched to be used for patching. Needed if multiple pixels in a row are an issue.

-
-
deginteger

Degree of polynomial if polynomial patching is used.

-
-
poly_rangeinteger

Number of pixels to include in the polynomial or interpolation fitting

-
-
-
-

Returns

-
-
float

The original data with the new patch values.

-
-
-
-
- -
-
-patch_pixel_1d(run, detector_key, pixel, mode='average', patch_range=4, deg=1, poly_range=6)[source]
-

EPIX detector pixel patching. -TODO: extend to patch regions instead of per pixel. -Parameters -———- -data : array_like

-
-

Array of shots

-
-
-
pixelinteger

Pixel point to be patched

-
-
modestring

Determined which mode to use for patching the pixel. Averaging works well.

-
-
patch_rangeinteger

pixels away from the pixel to be patched to be used for patching. Needed if multiple pixels in a row are an issue.

-
-
deginteger

Degree of polynomial if polynomial patching is used.

-
-
poly_rangeinteger

Number of pixels to include in the polynomial or interpolation fitting

-
-
-
-

Returns

-
-
float

The original data with the new patch values.

-
-
-
-
- -
-
-patch_pixels(run, detector_key, mode='average', patch_range=4, deg=1, poly_range=6, axis=1)[source]
-

Patches multiple pixels in detector data.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
modestr, optional

The mode of patching (‘average’, ‘polynomial’, or ‘interpolate’).

-
-
patch_rangeint, optional

The range around the pixel to use for patching (default is 4).

-
-
degint, optional

The degree of the polynomial for polynomial patching (default is 1).

-
-
poly_rangeint, optional

The range of pixels to use for polynomial or interpolation patching (default is 6).

-
-
axisint, optional

The axis along which to apply the patching (default is 1).

-
-
-
-
- -
-
-patch_pixels_1d(run, detector_key, mode='average', patch_range=4, deg=1, poly_range=6)[source]
-

Patches multiple pixels in 1D detector data.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
modestr, optional

The mode of patching (‘average’, ‘polynomial’, or ‘interpolate’).

-
-
patch_rangeint, optional

The range around the pixel to use for patching (default is 4).

-
-
degint, optional

The degree of the polynomial for polynomial patching (default is 1).

-
-
poly_rangeint, optional

The range of pixels to use for polynomial or interpolation patching (default is 6).

-
-
-
-
- -
-
-purge_keys(run, keys)[source]
-

Purges specific keys from the run to save memory. -This is specifically to remove the epix key immediately after processing it from the hdf5 file. -To avoid OOM. This is different than the purge all keys method which is used to purge many of the larger analysis steps.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
keyslist of str

The list of keys to purge.

-
-
-
-
- -
-
-reduce_detector_shots(run, detector_key, reduction_function=<function sum>, purge=True)[source]
-
- -
-
-reduce_detector_spatial(run, detector_key, shot_range=[0, None], rois=[[0, None]], reduction_function=<function sum>, purge=True, combine=True)[source]
-

Reduces the spatial dimension of detector data based on specified ROIs.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
shot_rangelist, optional

The range of shots to consider (default is [0, None]).

-
-
roislist of lists, optional

The list of ROIs (regions of interest) as pixel ranges (default is [[0, None]]).

-
-
reduction_functionfunction, optional

The function to apply for reduction (default is np.sum).

-
-
purgebool, optional

Whether to purge the original detector data after reduction (default is True).

-
-
combinebool, optional

Whether to combine ROIs (default is True).

-
-
-
-
- -
-
-reduce_detector_temporal(run, detector_key, timing_bin_key_indices, average=False)[source]
-

Reduces the temporal dimension of detector data based on timing bins.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
timing_bin_key_indicesstr

The key corresponding to the timing bin indices.

-
-
averagebool, optional

Whether to average the data within each bin (default is False).

-
-
-
-
- -
-
-separate_shots(run, detector_key, filter_keys)[source]
-

Separates shots into different datasets based on filters. -separate_shots(f,’epix_ROI_1’,[‘xray’,’laser’]) means find me the epix_ROI_1 images in shots that were X-ray but NOT laser. -If you wanted the inverse you would switch the order of the filter_keys.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
filter_keyslist of str

The list of filter keys to separate.

-
-
-
-
- -
-
-time_binning(run, bins, lxt_key='lxt_ttc', fast_delay_key='encoder', tt_correction_key='time_tool_correction')[source]
-

Bins data in time based on specified bins.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
binsarray-like

The bins to use for time binning.

-
-
lxt_keystr, optional

The key for the laser time delay data (default is ‘lxt_ttc’).

-
-
fast_delay_keystr, optional

The key for the fast delay data (default is ‘encoder’).

-
-
tt_correction_keystr, optional

The key for the time tool correction data (default is ‘time_tool_correction’).

-
-
-
-
- -
-
-union_shots(run, detector_key, filter_keys)[source]
-

Combines shots across multiple filters into a single array. -So union_shots(f,’timing_bin_indices’,[‘simultaneous’,’laser’]) -means go through the timing_bin_indices and find the ones that correspond to X-rays and laser shots.

-
-

Parameters

-
-
runspectroscopy_run

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
filter_keyslist of str

The list of filter keys to combine.

-
-
-
-
- -
- -
-
-class XSpect.XSpect_Analysis.XASAnalysis[source]
-

Bases: SpectroscopyAnalysis

-
-
-ccm_binning(run, ccm_bins_key, ccm_key='ccm')[source]
-

Generate CCM bin indices from CCM data and bins.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
ccm_bins_keystr

The key corresponding to the CCM bins.

-
-
ccm_keystr, optional

The key corresponding to the CCM data (default is ‘ccm’).

-
-
-
-
- -
-
-make_ccm_axis(run, energies)[source]
-

Generate CCM bins and centers from given energy values.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
energiesarray-like

Array of energy values to be used for creating CCM bins.

-
-
-
-
- -
-
-reduce_detector_ccm(run, detector_key, ccm_bin_key_indices, average=False, not_ccm=False)[source]
-

Reduce detector data by CCM bins.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
ccm_bin_key_indicesstr

The key corresponding to the CCM bin indices.

-
-
averagebool, optional

Whether to average the reduced data (default is False).

-
-
not_ccmbool, optional

Whether to indicate that CCM is not being used (default is False).

-
-
-
-
- -
-
-reduce_detector_ccm_temporal(run, detector_key, timing_bin_key_indices, ccm_bin_key_indices, average=True)[source]
-

Reduce detector data temporally and by CCM bins.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
timing_bin_key_indicesstr

The key corresponding to the timing bin indices.

-
-
ccm_bin_key_indicesstr

The key corresponding to the CCM bin indices.

-
-
averagebool, optional

Whether to average the reduced data (default is True).

-
-
-
-
- -
-
-reduce_detector_temporal(run, detector_key, timing_bin_key_indices, average=False)[source]
-

Reduce detector data temporally. Specifically the 1d detector output for XAS data.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
timing_bin_key_indicesstr

The key corresponding to the timing bin indices.

-
-
averagebool, optional

Whether to average the reduced data (default is False).

-
-
-
-
- -
-
-trim_ccm(run, threshold=120)[source]
-

Trim CCM values to remove bins with fewer shots than a specified threshold.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
thresholdint, optional

The minimum number of shots required to keep a CCM value (default is 120).

-
-
-
-
- -
- -
-
-class XSpect.XSpect_Analysis.XESAnalysis(xes_line='kbeta')[source]
-

Bases: SpectroscopyAnalysis

-
-
-make_energy_axis(run, energy_axis_length, A, R, mm_per_pixel=0.05, d=0.895)[source]
-

Determination of energy axis by pixels and crystal configuration

-
-

Parameters

-
-
Afloat

The detector to vH distance (mm) and can roughly float. This will affect the spectral offset.

-
-
Rfloat

The vH crystal radii (mm) and should not float. This will affect the spectral stretch.

-
-
pixel_arrayarray-like

Array of pixels to determine the energy of.

-
-
dfloat

Crystal d-spacing. To calculate, visit: spectra.tools/bin/controller.pl?body=Bragg_Angle_Calculator

-
-
-
-
- -
-
-normalize_xes(run, detector_key, pixel_range=[300, 550])[source]
-

Normalize XES data by summing the signal over a specified pixel range.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
pixel_rangelist of int, optional

The pixel range to sum over for normalization (default is [300, 550]).

-
-
-
-
- -
-
-reduce_det_scanvar(run, detector_key, scanvar_key, scanvar_bins_key)[source]
-

Reduce detector data by binning according to an arbitrary scan variable.

-

This method bins the detector data based on a specified scan variable and its corresponding bins. -The result is stored in the run object under a new attribute.

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data within the run object.

-
-
scanvar_keystr

The key corresponding to the scan variable indices.

-
-
scanvar_bins_keystr

The key corresponding to the scan variable bins.

-
-
-
-
-

Returns

-
-
None

The reduced data is stored in the run object with the key formatted as {detector_key}_scanvar_reduced.

-
-
-
-
- -
-
-reduce_detector_spatial(run, detector_key, shot_range=[0, None], rois=[[0, None]], reduction_function=<function sum>, purge=True, combine=True, adu_cutoff=3.0)[source]
-

Reduce spatial dimensions of detector data by combining or applying reduction functions over regions of interest (ROIs).

-
-

Parameters

-
-
runobject

The spectroscopy run instance.

-
-
detector_keystr

The key corresponding to the detector data.

-
-
shot_rangelist of int, optional

The range of shots to consider for reduction (default is [0, None]).

-
-
roislist of lists of int, optional

The regions of interest for spatial reduction (default is [[0, None]]).

-
-
reduction_functionfunction, optional

The function to use for reduction (default is np.sum).

-
-
purgebool, optional

Whether to purge the original detector data after reduction (default is True).

-
-
combinebool, optional

Whether to combine ROIs into a single reduced dataset (default is True).

-
-
adu_cutofffloat, optional

The ADU cutoff value for filtering (default is 3.0).

-
-
-
-
- -
- -
-
-class XSpect.XSpect_Analysis.experiment(lcls_run, hutch, experiment_id)[source]
-

Bases: object

-
-
-get_experiment_directory()[source]
-

Determines and returns the directory of the experiment based on the hutch and experiment ID. -It attempts the various paths LCLS has had over the years with recent S3DF paths being the first attempt.

-
-

Returns

-
-
str

The directory of the experiment.

-
-
-
-
-

Raises

-
-
Exception

If the directory cannot be found.

-
-
-
-
- -
- -
-
-class XSpect.XSpect_Analysis.spectroscopy_experiment(*args, **kwargs)[source]
-

Bases: experiment

-

A class to represent a spectroscopy experiment. -Trying to integrate methods that incorporate meta parameters of the experiment but did not follow through.

-
-
-add_detector(detector_name, detector_dimensions)[source]
-
- -
- -
-
-class XSpect.XSpect_Analysis.spectroscopy_run(spec_experiment, run, verbose=False, end_index=-1, start_index=0)[source]
-

Bases: object

-

A class to represent a run within a spectroscopy experiment. Not and LCLS run.

-
-
-close_h5()[source]
-

Closes the HDF5 file handle. -Again, avoiding memory issues.

-
- -
-
-get_run_shot_properties()[source]
-

Retrieves shot properties from the run file, including total shots and simultaneous laser and X-ray shots.

-
- -
-
-get_scan_val()[source]
-

Retrieves the scan variable from the HDF5 file of the run. -This is specifically for runengine scans that tag the variable in the hdf5 file. E.g. useful for processing alignment scans

-
- -
-
-load_run_key_delayed(keys, friendly_names, transpose=False, rois=None, combine=True)[source]
-

Loads specified keys from the run file into memory without immediate conversion to numpy arrays. -Supports applying multiple ROIs in one dimension that can be combined into a single mask or handled separately.

-
-

Parameters

-
-
keyslist

List of keys to load.

-
-
friendly_nameslist

Corresponding list of friendly names for the keys.

-
-
transposebool, optional

Flag to transpose the loaded data. Defaults to False.

-
-
roislist of lists, optional

List of ROIs (regions of interest) as pixel ranges along one dimension (default is None). -Each ROI should be in the form [start_col, end_col].

-
-
combinebool, optional

Whether to combine ROIs into a single mask. Defaults to True.

-
-
-
-
- -
-
-load_run_keys(keys, friendly_names)[source]
-

Loads specified keys from the run file into memory.

-
-

Parameters

-
-
keyslist

List of keys to load from the hdf5 file

-
-
friendly_nameslist

Corresponding list of friendly names for the keys. Some keys are special to the subsequent analyis e.g. epix and ipm.

-
-
-
-
- -
-
-load_sum_run_scattering(key, low=20, high=80)[source]
-

Sums the scattering data across the specified range.

-
-

Parameters

-
-
keystr

The key to sum the scattering data from.

-
-
lowint

Low index for summing

-
-
high: int

high index for summing -These indices should be chosen over the water ring or some scattering of interest.

-
-
-
-
- -
-
-purge_all_keys(keys_to_keep)[source]
-

Purges all keys from the object except those specified. Again avoid OOM in the analyis object.

-
-

Parameters

-
-
keys_to_keeplist

List of keys to retain.

-
-
-
-
- -
-
-update_status(update)[source]
-

Updates the status log for the run and appends it to the objects status/datetime attibutes. -If verbose then it prints it. -Parameters -———- -update : str

-
-

The status update message.

-
-
- -
- -
-
-

XSpect.XSpect_Controller module

-
-
-class XSpect.XSpect_Controller.BatchAnalysis(verbose=False)[source]
-

Bases: object

-
-
-add_filter(shot_type, filter_key, threshold)[source]
-
- -
-
-break_into_shot_ranges(increment)[source]
-
- -
-
-parse_run_shots(experiment, verbose=False)[source]
-
- -
-
-primary_analysis()[source]
-
- -
-
-primary_analysis_loop(experiment, verbose=False)[source]
-
- -
-
-primary_analysis_parallel_loop(cores, experiment, verbose=False)[source]
-
- -
-
-primary_analysis_parallel_range(cores, experiment, increment, start_index=None, end_index=None, verbose=False, method=None)[source]
-
- -
-
-run_parser(run_array)[source]
-
- -
-
-set_key_aliases(keys=['tt/ttCorr', 'epics/lxt_ttc', 'enc/lasDelay', 'ipm4/sum', 'tt/AMPL', 'epix_2/ROI_0_area'], names=['time_tool_correction', 'lxt_ttc', 'encoder', 'ipm', 'time_tool_ampl', 'epix'])[source]
-
- -
-
-update_status(update)[source]
-
- -
- -
-
-class XSpect.XSpect_Controller.ScanAnalysis_1D(*args, **kwargs)[source]
-

Bases: BatchAnalysis

-
-
-primary_analysis(experiment, run, verbose=False)[source]
-
- -
- -
-
-class XSpect.XSpect_Controller.ScanAnalysis_1D_XES(*args, **kwargs)[source]
-

Bases: BatchAnalysis

-
-
-primary_analysis(experiment, run, verbose=False)[source]
-
- -
- -
-
-class XSpect.XSpect_Controller.XASBatchAnalysis(*args, **kwargs)[source]
-

Bases: BatchAnalysis

-
-
-primary_analysis(experiment, run, verbose=False)[source]
-
- -
- -
-
-class XSpect.XSpect_Controller.XASBatchAnalysis_1D_ccm(*args, **kwargs)[source]
-

Bases: BatchAnalysis

-
-
-primary_analysis(experiment, run, verbose=False)[source]
-
- -
- -
-
-class XSpect.XSpect_Controller.XASBatchAnalysis_1D_time(*args, **kwargs)[source]
-

Bases: BatchAnalysis

-
-
-primary_analysis(experiment, run, verbose=False)[source]
-
- -
- -
-
-class XSpect.XSpect_Controller.XESBatchAnalysis[source]
-

Bases: BatchAnalysis

-
-
-primary_analysis(experiment, run, verbose=False, start_index=None, end_index=None)[source]
-
- -
- -
-
-class XSpect.XSpect_Controller.XESBatchAnalysisRotation[source]
-

Bases: XESBatchAnalysis

-
-
-hit_find(experiment, run, verbose=False, start_index=None, end_index=None)[source]
-
- -
-
-primary_analysis(experiment, run, verbose=False, start_index=None, end_index=None)[source]
-
- -
-
-primary_analysis_range(experiment, run, shot_ranges, verbose=False, method=None)[source]
-
- -
-
-primary_analysis_static(run, experiment, verbose=False, start_index=None, end_index=None)[source]
-
- -
-
-primary_analysis_static_parallel_loop(cores, experiment, verbose=False)[source]
-
- -
- -
-
-XSpect.XSpect_Controller.analyze_single_run(args)[source]
-
- -
-
-

XSpect.XSpect_Diagnostics module

-
-
-class XSpect.XSpect_Diagnostics.diagnostics(run, exp, keys, friendly_names)[source]
-

Bases: plotting

-
-
-adu_histogram(nshots, thresholds, ROIopt=False, energy_dispersive_axis='vert')[source]
-
- -
-
-ipm_histogram(thresholds)[source]
-
- -
-
-load_run_keys()[source]
-
- -
-
-ttAMPL_histogram(thresholds)[source]
-
- -
-
-xas_ROI(nshots, horiz_limits=[], vert_limits=[], setrois=False)[source]
-
- -
-
-xes_ROI(nshots, kb_limits=[], ka_limits=[], setrois=False, energy_dispersive_axis='vert', angle=0)[source]
-
- -
- -
-
-class XSpect.XSpect_Diagnostics.plotting[source]
-

Bases: object

-
-
-hplot(data, thresholds, plt_title, leg_title, xlabel, yscale)[source]
-
- -
-
-roiview(data, thres, plt_type, energy_dispersive_axis='vert')[source]
-
- -
- -
-
-

XSpect.XSpect_PostProcessing module

-
-
-class XSpect.XSpect_PostProcessing.analysis_functions[source]
-

Bases: object

-
-
-expfunc(x, k, amp=[], x0=0)[source]
-
- -
-
-expfunc_heaviside(x, k, amp=[], x0=0)[source]
-
- -
-
-gaussfunc(x, center, sigma)[source]
-
- -
-
-gaussfunc_norm(x, center, sigma)[source]
-
- -
-
-irfconv(x, k, center, sigma, amp=[])[source]
-
- -
-
-irfconv_ana(x, k, center, sigma, amp=[])[source]
-
- -
-
-kmatsolver(kmatrix, x, k, X0, center, sigma, irf_option='numerical', printopt=True)[source]
-
- -
- -
-
-class XSpect.XSpect_PostProcessing.plotting[source]
-

Bases: object

-
- -
-
-class XSpect.XSpect_PostProcessing.post_analysis[source]
-

Bases: analysis_functions

-
-
-construct_theta(theta_parser)[source]
-
- -
-
-parse_theta(k=[], center=[], sigma=[], amplitudes=[])[source]
-
- -
-
-read_theta(theta, theta_parser)[source]
-
- -
-
-svdplot(xval, yval, data, ncomp)[source]
-
- -
-
-svdreconstruct(data, ncomp)[source]
-
- -
-
-targetanalysis_run(data, x, kmatrix, k_in, center_in, sigma_in, X0_in, y=[], bounds_dict=None)[source]
-
- -
-
-targetobjective(theta, x, kmatrix, X0, theta_parser, data)[source]
-
- -
-
-varproj(kmatrix, x, k, X0, center, sigma, data)[source]
-
- -
- -
-
-

XSpect.XSpect_Visualization module

-
-
-class XSpect.XSpect_Visualization.SpectroscopyVisualization[source]
-

Bases: object

-
-
-plot_2d_difference_spectrum(run, detector_keys)[source]
-
- -
-
-plot_2d_spectrum(run, detector_key)[source]
-
- -
- -
-
-class XSpect.XSpect_Visualization.XASVisualization[source]
-

Bases: SpectroscopyVisualization

-
-
-combine_spectra(xas_analysis, xas_laser_key, xas_key, norm_laser_key, norm_key, interpolate=False)[source]
-
- -
-
-plot_1d_difference_spectrum(xas_analysis)[source]
-
- -
-
-plot_1d_difference_time(xas_analysis)[source]
-
- -
-
-plot_2d_difference_spectrum(xas_analysis, vmin=None, vmax=None)[source]
-
- -
-
-plot_XAS(run, detector_key, ccm_key)[source]
-
- -
- -
-
-class XSpect.XSpect_Visualization.XESVisualization[source]
-

Bases: SpectroscopyVisualization

-
-
-combine_spectra(xes_analysis, xes_key, xes_laser_key)[source]
-
- -
-
-combine_static_spectra(xes_analysis, xes_key)[source]
-
- -
-
-plot_1d_XES(run, detector_key, target_key, low=-inf, high=inf, axis=0)[source]
-
- -
-
-plot_2d_difference_spectrum(xes_analysis)[source]
-
- -
- -
-
-

Module contents

-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/XSpect/XSpect_Analysis.html b/docs/_build/html/_modules/XSpect/XSpect_Analysis.html deleted file mode 100644 index eb27e30..0000000 --- a/docs/_build/html/_modules/XSpect/XSpect_Analysis.html +++ /dev/null @@ -1,1279 +0,0 @@ - - - - - - XSpect.XSpect_Analysis — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for XSpect.XSpect_Analysis

-import h5py
-import numpy as np
-import os
-import time
-from datetime import datetime
-
-
-
-[docs] -class experiment: - def __init__(self, lcls_run, hutch, experiment_id): - """ - Initializes an experiment instance. - - Parameters - ---------- - lcls_run : str - LCLS run identifier. The LCLS run not the scan/run. Example: 21 - hutch : str - Hutch name. Example: xcs - experiment_id : str - Experiment identifier. Example: xcsl1004021 - """ - self.lcls_run = lcls_run - self.hutch = hutch - self.experiment_id = experiment_id - self.get_experiment_directory() - -
-[docs] - def get_experiment_directory(self): - """ - Determines and returns the directory of the experiment based on the hutch and experiment ID. - It attempts the various paths LCLS has had over the years with recent S3DF paths being the first attempt. - - Returns - ------- - str - The directory of the experiment. - - Raises - ------ - Exception - If the directory cannot be found. - """ - experiment_directories = [ - '/sdf/data/lcls/ds/%s/%s/hdf5/smalldata', - '/reg/data/drpsrcf/%s/%s/scratch/hdf5/smalldata', - '/cds/data/drpsrcf/%s/%s/scratch/hdf5/smalldata', - '/reg/d/psdm/%s/%s/hdf5/smalldata' - ] - for directory in experiment_directories: - experiment_directory = directory % (self.hutch, self.experiment_id) - if os.path.exists(experiment_directory) and os.listdir(experiment_directory): - self.experiment_directory = experiment_directory - return experiment_directory - raise Exception("Unable to find experiment directory.")
-
- - - -
-[docs] -class spectroscopy_experiment(experiment): - """ - A class to represent a spectroscopy experiment. - Trying to integrate methods that incorporate meta parameters of the experiment but did not follow through. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - -
-[docs] - def add_detector(self, detector_name, detector_dimensions): - self.detector_name = detector_name - self.detector_dimensions = detector_dimensions
-
- - - -
-[docs] -class spectroscopy_run: - """ - A class to represent a run within a spectroscopy experiment. Not and LCLS run. - """ - - def __init__(self, spec_experiment, run, verbose=False, end_index=-1, start_index=0): - """ - Initializes a spectroscopy run instance. - - Parameters - ---------- - spec_experiment : spectroscopy_experiment - The parent spectroscopy experiment. - run : int - The run number. - verbose : bool, optional - Flag for verbose output used for printing all of the status updates. - These statuses are also available in the object itself. Defaults to False. - end_index : int, optional - Index to stop processing data. Defaults to -1. - start_index : int, optional - Index to start processing data. Defaults to 0. - These indices are used for batch analysis. - """ - self.spec_experiment = spec_experiment - self.run_number = run - self.run_file = '%s/%s_Run%04d.h5' % (self.spec_experiment.experiment_directory, - self.spec_experiment.experiment_id, self.run_number) - self.status = ['New analysis of run %d located in: %s' % - (self.run_number, self.run_file)] - self.status_datetime = [datetime.now().strftime("%Y-%m-%d %H:%M:%S")] - self.verbose = verbose - self.end_index = end_index - self.start_index = start_index - -
-[docs] - def get_scan_val(self): - """ - Retrieves the scan variable from the HDF5 file of the run. - This is specifically for runengine scans that tag the variable in the hdf5 file. E.g. useful for processing alignment scans - """ - with h5py.File(self.run_file, 'r') as fh: - self.scan_var = fh['scan/scan_variable']
- - -
-[docs] - def update_status(self, update,): - """ - Updates the status log for the run and appends it to the objects status/datetime attibutes. - If verbose then it prints it. - Parameters - ---------- - update : str - The status update message. - """ - self.status.append(update) - self.status_datetime.append( - datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - if self.verbose: - print(update)
- - -
-[docs] - def get_run_shot_properties(self): - """ - Retrieves shot properties from the run file, including total shots and simultaneous laser and X-ray shots. - """ - with h5py.File(self.run_file, 'r') as fh: - self.total_shots = fh['lightStatus/xray'][self.start_index:self.end_index].shape[0] - xray_total = np.sum(fh['lightStatus/xray'] - [self.start_index:self.end_index]) - laser_total = np.sum(fh['lightStatus/laser'] - [self.start_index:self.end_index]) - self.xray = np.array(fh['lightStatus/xray'] - [self.start_index:self.end_index]) - self.laser = np.array( - fh['lightStatus/laser'][self.start_index:self.end_index]) - self.simultaneous = np.logical_and(self.xray, self.laser) - - self.run_shots = {'Total': self.total_shots, - 'X-ray Total': xray_total, 'Laser Total': laser_total} - self.update_status('Obtained shot properties')
- - -
-[docs] - def load_run_keys(self, keys, friendly_names): - """ - Loads specified keys from the run file into memory. - - Parameters - ---------- - keys : list - List of keys to load from the hdf5 file - friendly_names : list - Corresponding list of friendly names for the keys. Some keys are special to the subsequent analyis e.g. epix and ipm. - """ - start = time.time() - with h5py.File(self.run_file, 'r') as fh: - for key, name in zip(keys, friendly_names): - - try: - setattr(self, name, np.array( - fh[key][self.start_index:self.end_index])) - except KeyError as e: - self.update_status('Key does not exist: %s' % e.args[0]) - except MemoryError: - setattr(self, name, fh[key]) - self.update_status( - 'Out of memory error while loading key: %s. Not converted to np.array.' % key) - end = time.time() - self.update_status( - 'HDF5 import of keys completed. Time: %.02f seconds' % (end-start))
- - -
-[docs] - def load_run_key_delayed(self, keys, friendly_names, transpose=False, rois=None, combine=True): - """ - Loads specified keys from the run file into memory without immediate conversion to numpy arrays. - Supports applying multiple ROIs in one dimension that can be combined into a single mask or handled separately. - - Parameters - ---------- - keys : list - List of keys to load. - friendly_names : list - Corresponding list of friendly names for the keys. - transpose : bool, optional - Flag to transpose the loaded data. Defaults to False. - rois : list of lists, optional - List of ROIs (regions of interest) as pixel ranges along one dimension (default is None). - Each ROI should be in the form [start_col, end_col]. - combine : bool, optional - Whether to combine ROIs into a single mask. Defaults to True. - """ - start = time.time() - fh = h5py.File(self.run_file, 'r') - - for key, name in zip(keys, friendly_names): - try: - # Load the data from the file for the given key - data = fh[key][self.start_index:self.end_index, :, :] - - # Apply one-dimensional ROIs if specified - if rois is not None: - if combine: - # Combine multiple ROIs into a single mask - # Mask along the third dimension (spatial) - mask = np.zeros(data.shape[2], dtype=bool) - for roi in rois: - start_col, end_col = roi - mask[start_col:end_col] = True - # Apply the mask to select the ROI from the third dimension - data = data[:, :, mask] - else: - # Handle each ROI separately, storing the results as different attributes - for idx, roi in enumerate(rois): - start_col, end_col = roi - roi_data = data[:, :, start_col:end_col] - setattr(self, f"{name}_ROI_{idx+1}", roi_data) - - setattr(self, name, data) - - if transpose: - setattr(self, name, np.transpose(data, axes=(1, 2))) - - except KeyError as e: - self.update_status(f'Key does not exist: {e.args[0]}') - except MemoryError: - setattr(self, name, fh[key] - [self.start_index:self.end_index, :, :]) - self.update_status(f'Out of memory error while loading key: {key}. Not converted to np.array.') - - end = time.time() - self.update_status(f'HDF5 import of keys completed. Time: {end - start:.02f} seconds') - self.h5 = fh
- - -
-[docs] - def load_sum_run_scattering(self, key, low=20, high=80): - """ - Sums the scattering data across the specified range. - - Parameters - ---------- - key : str - The key to sum the scattering data from. - low : int - Low index for summing - high: int - high index for summing - These indices should be chosen over the water ring or some scattering of interest. - """ - with h5py.File(self.run_file, 'r') as fh: - setattr(self, 'scattering', np.nansum( - np.nansum(fh[key][:, :, low:high], axis=1), axis=1))
- - -
-[docs] - def close_h5(self): - """ - Closes the HDF5 file handle. - Again, avoiding memory issues. - """ - self.h5.close() - del self.h5
- - -
-[docs] - def purge_all_keys(self, keys_to_keep): - """ - Purges all keys from the object except those specified. Again avoid OOM in the analyis object. - - Parameters - ---------- - keys_to_keep : list - List of keys to retain. - """ - - new_dict = {attr: value for attr, - value in self.__dict__.items() if attr in keys_to_keep} - self.__dict__ = new_dict
-
- - - -
-[docs] -class SpectroscopyAnalysis: - """ - A class to perform analysis on spectroscopy data. - """ - - def __init__(self): - pass - -
-[docs] - def bin_uniques(self, run, key): - """ - Bins unique values for a given key within a run. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - key : str - The key for which unique values are to be binned. - """ - vals = getattr(run, key) - bins = np.unique(vals) - addon = (bins[-1] - bins[-2])/2 # add on energy - # elist2 will be elist with dummy value at end - bins2 = np.append(bins, bins[-1]+addon) - bins_center = np.empty_like(bins2) - for ii in np.arange(bins.shape[0]): - if ii == 0: - bins_center[ii] = bins2[ii] - (bins2[ii+1] - bins2[ii])/2 - else: - bins_center[ii] = bins2[ii] - (bins2[ii] - bins2[ii-1])/2 - bins_center[-1] = bins2[-1] - - setattr(run, 'scanvar_indices', np.digitize(vals, bins_center)) - setattr(run, 'scanvar_bins', bins_center)
- - -
-[docs] - def filter_shots(self, run, shot_mask_key, filter_key='ipm', threshold=1.0E4): - """ - Filters shots based on a given threshold. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - shot_mask_key : str - The key corresponding to the shot mask. An example being [xray,simultaneous,laser] for all x-ray shots - filter_key : str, optional - The key corresponding to the filter data (default is 'ipm'). - threshold : float, optional - The threshold value for filtering (default is 1.0E4). - - So if we filter: xray,ipm,1E4 then X-ray shots will be filtered out if the ipm is below 1E4. - """ - shot_mask = getattr(run, shot_mask_key) - count_before = np.sum(shot_mask) - filter_mask = getattr(run, filter_key) - nan_mask = np.isnan(filter_mask) - filtered_shot_mask = shot_mask * \ - (filter_mask > threshold) * (~nan_mask) - count_after = np.sum(filtered_shot_mask) - setattr(run, shot_mask_key, filtered_shot_mask) - run.update_status('Mask: %s has been filtered on %s by minimum threshold: %0.3f\nShots removed: %d' % (shot_mask_key, filter_key, threshold, count_before-count_after))
- - -
-[docs] - def filter_nan(self, run, shot_mask_key, filter_key='ipm'): - """ - A specific filtering implementation for Nans due to various DAQ issues. - Filters out shots with NaN values in the specified filter. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - shot_mask_key : str - The key corresponding to the shot mask. - filter_key : str, optional - The key corresponding to the filter data (default is 'ipm'). - """ - shot_mask = getattr(run, shot_mask_key) - count_before = np.sum(shot_mask) - filter_mask = getattr(run, filter_key) - filtered_shot_mask = shot_mask * (filter_mask > threshold) - count_after = np.sum(filtered_shot_mask) - setattr(run, shot_mask_key, filtered_shot_mask) - run.update_status('Mask: %s has been filtered on %s by minimum threshold: %0.3f\nShots removed: %d' % (shot_mask_key, filter_key, threshold, count_before-count_after))
- - -
-[docs] - def filter_detector_adu(self, run, detector, adu_threshold=3.0): - """ - Filters is a misnomer compared to the other filter functions. - This sets detector pixel values below a threshold to 0. - Specifically, to remove 0-photon noise from detectors. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - detector : str - The key corresponding to the detector data. - adu_threshold : float or list of float, optional - The ADU threshold for filtering. Can be a single value or a range (default is 3.0). - - Returns - ------- - np.ndarray - The filtered detector data. - """ - detector_images = getattr(run, detector) - if isinstance(adu_threshold, list): - detector_images_adu = detector_images * \ - (detector_images > adu_threshold[0]) - detector_images_adu = detector_images_adu * \ - (detector_images_adu < adu_threshold[1]) - run.update_status('Key: %s has been adu filtered by thresholds: %f,%f' % (detector, adu_threshold[0], adu_threshold[1])) - else: - detector_images_adu = detector_images * \ - (detector_images > adu_threshold) - run.update_status('Key: %s has been adu filtered by threshold: %f' % (detector, adu_threshold)) - - setattr(run, detector, detector_images_adu) - - return detector_images_adu
- - -
-[docs] - def purge_keys(self, run, keys): - """ - Purges specific keys from the run to save memory. - This is specifically to remove the epix key immediately after processing it from the hdf5 file. - To avoid OOM. This is different than the purge all keys method which is used to purge many of the larger analysis steps. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - keys : list of str - The list of keys to purge. - """ - for detector_key in keys: - setattr(run, detector_key, None) - run.update_status(f"Purged key to save room: {detector_key}")
- - -
-[docs] - def reduce_detector_shots(self, run, detector_key, reduction_function=np.sum, purge=True): - detector = getattr(run, detector_key) - reduced_data = reduction_function(detector, axis=0) - run.update_status(f"Reduced detector by shots: {detector_key} with number of shots: {np.shape(detector)}") - setattr(run, f"{detector_key}_summed", reduced_data) - if purge: - setattr(run, detector_key, None) - run.update_status(f"Purged key to save room: {detector_key}")
- - -
-[docs] - def reduce_detector_spatial(self, run, detector_key, shot_range=[0, None], rois=[[0, None]], reduction_function=np.sum, purge=True, combine=True): - """ - Reduces the spatial dimension of detector data based on specified ROIs. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - shot_range : list, optional - The range of shots to consider (default is [0, None]). - rois : list of lists, optional - The list of ROIs (regions of interest) as pixel ranges (default is [[0, None]]). - reduction_function : function, optional - The function to apply for reduction (default is np.sum). - purge : bool, optional - Whether to purge the original detector data after reduction (default is True). - combine : bool, optional - Whether to combine ROIs (default is True). - """ - detector = getattr(run, detector_key) - if combine: - - # Combined ROI spanning the first and last ROI - roi_combined = [rois[0][0], rois[-1][1]] - mask = np.zeros(detector.shape[2], dtype=bool) - for roi in rois: - mask[roi[0]:roi[1]] = True - masked_data = detector[shot_range[0]:shot_range[1], :, :][:, :, mask] - reduced_data = reduction_function(masked_data, axis=2) - roi_indices = ', '.join([f"{roi[0]}-{roi[1]}" for roi in rois]) - run.update_status(f"Spatially reduced detector: {detector_key} with combined ROI indices: {roi_indices}") - setattr(run, f"{detector_key}_ROI_1", reduced_data) - else: - for idx, roi in enumerate(rois): - data_chunk = detector[shot_range[0]:shot_range[1], roi[0]:roi[1]] - reduced_data = reduction_function(data_chunk, **kwargs) - if roi[1] is None: - roi[1] = detector.shape[1] - 1 - run.update_status(f"Spatially reduced detector: {detector_key} with ROI: {roi[0]}, {roi[1]}") - setattr(run, f"{detector_key}_ROI_{idx+1}", reduced_data) - if purge: - # pass - setattr(run, detector_key, None) - # delattr(run, detector_key) - # del run.detector_key - run.update_status(f"Purged key to save room: {detector_key}")
- - -
-[docs] - def time_binning(self, run, bins, lxt_key='lxt_ttc', fast_delay_key='encoder', tt_correction_key='time_tool_correction'): - """ - Bins data in time based on specified bins. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - bins : array-like - The bins to use for time binning. - lxt_key : str, optional - The key for the laser time delay data (default is 'lxt_ttc'). - fast_delay_key : str, optional - The key for the fast delay data (default is 'encoder'). - tt_correction_key : str, optional - The key for the time tool correction data (default is 'time_tool_correction'). - """ - if lxt_key == None: - run.delays = 0 + getattr(run, fast_delay_key) + \ - getattr(run, tt_correction_key) - else: - run.delays = getattr(run, lxt_key)*1.0e12 + getattr(run, - fast_delay_key) + getattr(run, tt_correction_key) - run.time_bins = bins - run.timing_bin_indices = np.digitize(run.delays, bins)[:] - run.update_status('Generated timing bins from %f to %f in %d steps.' % ( - np.min(bins), np.max(bins), len(bins)))
- - -
-[docs] - def union_shots(self, run, detector_key, filter_keys): - """ - Combines shots across multiple filters into a single array. - So union_shots(f,'timing_bin_indices',['simultaneous','laser']) - means go through the timing_bin_indices and find the ones that correspond to X-rays and laser shots. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - filter_keys : list of str - The list of filter keys to combine. - """ - detector = getattr(run, detector_key) - - if isinstance(filter_keys, list): - mask = np.logical_and.reduce( - [getattr(run, k) for k in filter_keys]) - else: - mask = getattr(run, filter_keys) - filtered_detector = detector[mask] - setattr(run, detector_key + '_' + - '_'.join(filter_keys), filtered_detector) - run.update_status('Shots combined for detector %s on filters: %s and %s into %s' % ( - detector_key, filter_keys[0], filter_keys[1], detector_key + '_' + '_'.join(filter_keys)))
- - -
-[docs] - def separate_shots(self, run, detector_key, filter_keys): - """ - Separates shots into different datasets based on filters. - separate_shots(f,'epix_ROI_1',['xray','laser']) means find me the epix_ROI_1 images in shots that were X-ray but NOT laser. - If you wanted the inverse you would switch the order of the filter_keys. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - filter_keys : list of str - The list of filter keys to separate. - """ - detector = getattr(run, detector_key) - if isinstance(filter_keys, list): - mask1 = getattr(run, filter_keys[0]) - mask2 = np.logical_not(getattr(run, filter_keys[1])) - mask = np.logical_and(mask1, mask2) - else: - mask = getattr(run, filter_keys) - filtered_detector = detector[mask] - setattr(run, detector_key + '_' + - filter_keys[0]+'_not_'+filter_keys[1], filtered_detector) - run.update_status('Shots (%d) separated for detector %s on filters: %s and %s into %s' % (np.sum( - mask), detector_key, filter_keys[0], filter_keys[1], detector_key + '_' + '_'.join(filter_keys)))
- - -
-[docs] - def reduce_detector_temporal(self, run, detector_key, timing_bin_key_indices, average=False): - """ - Reduces the temporal dimension of detector data based on timing bins. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - timing_bin_key_indices : str - The key corresponding to the timing bin indices. - average : bool, optional - Whether to average the data within each bin (default is False). - """ - detector = getattr(run, detector_key) - indices = getattr(run, timing_bin_key_indices) - # print(len(detector.shape)) - expected_length = len(run.time_bins)+1 - if len(detector.shape) < 2: - reduced_array = np.zeros((expected_length)) - elif len(detector.shape) < 3: - reduced_array = np.zeros((expected_length, detector.shape[1])) - elif len(detector.shape) == 3: - reduced_array = np.zeros( - (expected_length, detector.shape[1], detector.shape[2])) - - counts = np.bincount(indices) - if average: - np.add.at(reduced_array, indices, detector) - reduced_array /= counts[:, None] - else: - np.add.at(reduced_array, indices, detector) - setattr(run, detector_key+'_time_binned', reduced_array) - run.update_status('Detector %s binned in time into key: %s from detector shape: %s to reduced shape: %s' % ( - detector_key, detector_key+'_time_binned', detector.shape, reduced_array.shape))
- - -
-[docs] - def patch_pixels(self, run, detector_key, mode='average', patch_range=4, deg=1, poly_range=6, axis=1): - """ - Patches multiple pixels in detector data. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - mode : str, optional - The mode of patching ('average', 'polynomial', or 'interpolate'). - patch_range : int, optional - The range around the pixel to use for patching (default is 4). - deg : int, optional - The degree of the polynomial for polynomial patching (default is 1). - poly_range : int, optional - The range of pixels to use for polynomial or interpolation patching (default is 6). - axis : int, optional - The axis along which to apply the patching (default is 1). - """ - for pixel in self.pixels_to_patch: - self.patch_pixel(run, detector_key, pixel, mode, - patch_range, deg, poly_range, axis=axis)
- - -
-[docs] - def patch_pixel(self, run, detector_key, pixel, mode='average', patch_range=4, deg=1, poly_range=6, axis=1): - """ - EPIX detector pixel patching. - TODO: extend to patch regions instead of per pixel. - Parameters - ---------- - data : array_like - Array of shots - pixel : integer - Pixel point to be patched - mode : string - Determined which mode to use for patching the pixel. Averaging works well. - patch_range : integer - pixels away from the pixel to be patched to be used for patching. Needed if multiple pixels in a row are an issue. - deg : integer - Degree of polynomial if polynomial patching is used. - poly_range : integer - Number of pixels to include in the polynomial or interpolation fitting - Returns - ------- - float - The original data with the new patch values. - """ - data = getattr(run, detector_key) - if mode == 'average': - neighbor_values = data[:, pixel - - patch_range:pixel + patch_range + 1, :] - new_val = np.sum(neighbor_values, axis=1) / \ - neighbor_values.shape[1] - - if axis == 1: - data[:, pixel, :] = new_val - elif axis == 2: - data[:, :, pixel] = new_val - elif mode == 'polynomial': - patch_x = np.arange(pixel - patch_range - poly_range, - pixel + patch_range + poly_range + 1, 1) - patch_range_weights = np.ones(len(patch_x)) - patch_range_weights[pixel - patch_range - - poly_range:pixel + patch_range + poly_range] = 0.001 - coeffs = np.polyfit(patch_x, data[pixel - patch_range - poly_range:pixel + patch_range + poly_range + 1, :], deg, - w=patch_range_weights) - data[pixel, :] = np.polyval(coeffs, pixel) - elif mode == 'interpolate': - patch_x = np.arange(pixel - patch_range - poly_range, - pixel + patch_range + poly_range + 1, 1) - interp = interp1d(patch_x, data[pixel - patch_range - poly_range:pixel + patch_range + poly_range + 1, :], - kind='quadratic') - data[pixel, :] = interp(pixel) - setattr(run, detector_key, data) - run.update_status( - 'Detector %s pixel %d patched. Old value.' % (detector_key, pixel))
- - -
-[docs] - def patch_pixels_1d(self, run, detector_key, mode='average', patch_range=4, deg=1, poly_range=6): - """ - Patches multiple pixels in 1D detector data. - - Parameters - ---------- - run : spectroscopy_run - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - mode : str, optional - The mode of patching ('average', 'polynomial', or 'interpolate'). - patch_range : int, optional - The range around the pixel to use for patching (default is 4). - deg : int, optional - The degree of the polynomial for polynomial patching (default is 1). - poly_range : int, optional - The range of pixels to use for polynomial or interpolation patching (default is 6). - """ - for pixel in self.pixels_to_patch: - self.patch_pixel_1d(run, detector_key, pixel, - mode, patch_range, deg, poly_range)
- - -
-[docs] - def patch_pixel_1d(self, run, detector_key, pixel, mode='average', patch_range=4, deg=1, poly_range=6): - """ - EPIX detector pixel patching. - TODO: extend to patch regions instead of per pixel. - Parameters - ---------- - data : array_like - Array of shots - pixel : integer - Pixel point to be patched - mode : string - Determined which mode to use for patching the pixel. Averaging works well. - patch_range : integer - pixels away from the pixel to be patched to be used for patching. Needed if multiple pixels in a row are an issue. - deg : integer - Degree of polynomial if polynomial patching is used. - poly_range : integer - Number of pixels to include in the polynomial or interpolation fitting - Returns - ------- - float - The original data with the new patch values. - """ - data = getattr(run, detector_key) - if mode == 'average': - neighbor_values = data[:, pixel - - patch_range:pixel + patch_range + 1] - data[:, pixel] = np.sum( - neighbor_values, axis=1) / neighbor_values.shape[1] - elif mode == 'polynomial': - patch_x = np.arange(pixel - patch_range - poly_range, - pixel + patch_range + poly_range + 1, 1) - patch_range_weights = np.ones(len(patch_x)) - patch_range_weights[pixel - patch_range - - poly_range:pixel + patch_range + poly_range] = 0.001 - coeffs = np.polyfit(patch_x, data[pixel - patch_range - poly_range:pixel + patch_range + poly_range + 1], deg, - w=patch_range_weights) - data[pixel, :] = np.polyval(coeffs, pixel) - elif mode == 'interpolate': - patch_x = np.arange(pixel - patch_range - poly_range, - pixel + patch_range + poly_range + 1, 1) - interp = interp1d(patch_x, data[pixel - patch_range - poly_range:pixel + patch_range + poly_range + 1, :], - kind='quadratic') - data[pixel, :] = interp(pixel) - setattr(run, detector_key, data) - run.update_status('Detector %s pixel %d patched.' % - (detector_key, pixel))
-
- - - -
-[docs] -class XESAnalysis(SpectroscopyAnalysis): - def __init__(self, xes_line='kbeta'): - self.xes_line = xes_line - pass - -
-[docs] - def normalize_xes(self, run, detector_key, pixel_range=[300, 550]): - """ - Normalize XES data by summing the signal over a specified pixel range. - - Parameters - ---------- - run : object - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - pixel_range : list of int, optional - The pixel range to sum over for normalization (default is [300, 550]). - """ - detector = getattr(run, detector_key) - row_sum = np.sum(detector[:, pixel_range[0]:pixel_range[1]], axis=1) - normed_main = np.divide(detector, row_sum[:, np.newaxis]) - setattr(run, detector_key+'_normalized', normed_main)
- - -
-[docs] - def make_energy_axis(self, run, energy_axis_length, A, R, mm_per_pixel=0.05, d=0.895): - """ - Determination of energy axis by pixels and crystal configuration - - Parameters - ---------- - A : float - The detector to vH distance (mm) and can roughly float. This will affect the spectral offset. - R : float - The vH crystal radii (mm) and should not float. This will affect the spectral stretch. - pixel_array : array-like - Array of pixels to determine the energy of. - d : float - Crystal d-spacing. To calculate, visit: spectra.tools/bin/controller.pl?body=Bragg_Angle_Calculator - - """ - pix = mm_per_pixel - gl = np.arange(energy_axis_length, dtype=np.float64) - gl *= pix - ll = gl / 2 - (np.amax(gl) - np.amin(gl)) / 4 - factor = 1.2398e4 - xaxis = factor / (2.0 * d * np.sin(np.arctan(R / (ll + A)))) - - setattr(run, self.xes_line+'_energy', xaxis[::-1]) - run.update_status('XES energy axis generated for %s' % (self.xes_line))
- - -
-[docs] - def reduce_detector_spatial(self, run, detector_key, shot_range=[0, None], rois=[[0, None]], reduction_function=np.sum, purge=True, combine=True, adu_cutoff=3.0): - """ - Reduce spatial dimensions of detector data by combining or applying reduction functions over regions of interest (ROIs). - - Parameters - ---------- - run : object - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - shot_range : list of int, optional - The range of shots to consider for reduction (default is [0, None]). - rois : list of lists of int, optional - The regions of interest for spatial reduction (default is [[0, None]]). - reduction_function : function, optional - The function to use for reduction (default is np.sum). - purge : bool, optional - Whether to purge the original detector data after reduction (default is True). - combine : bool, optional - Whether to combine ROIs into a single reduced dataset (default is True). - adu_cutoff : float, optional - The ADU cutoff value for filtering (default is 3.0). - """ - detector = getattr(run, detector_key) - if combine: - - # Combined ROI spanning the first and last ROI - roi_combined = [rois[0][0], rois[-1][1]] - mask = np.zeros(detector.shape[2], dtype=bool) - for roi in rois: - mask[roi[0]:roi[1]] = True - masked_data = detector[shot_range[0]:shot_range[1], :, :][:, :, mask] - # masked_data = masked_data * (masked_data > adu_cutoff) - # Note the adu filtering should be handled at the controller level - reduced_data = reduction_function(masked_data, axis=2) - roi_indices = ', '.join([f"{roi[0]}-{roi[1]}" for roi in rois]) - run.update_status(f"Spatially reduced detector: {detector_key} with combined ROI indices: {roi_indices}") - setattr(run, f"{detector_key}_ROI_1", reduced_data) - else: - for idx, roi in enumerate(rois): - data_chunk = detector[shot_range[0]:shot_range[1], roi[0]:roi[1]] - reduced_data = reduction_function(data_chunk, **kwargs) - if roi[1] is None: - roi[1] = detector.shape[1] - 1 - run.update_status(f"Spatially reduced detector: {detector_key} with ROI: {roi[0]}, {roi[1]}") - setattr(run, f"{detector_key}_ROI_{idx+1}", reduced_data) - if purge: - # pass - setattr(run, detector_key, None) - # delattr(run, detector_key) - # del run.detector_key - run.update_status(f"Purged key to save room: {detector_key}")
- - -
-[docs] - def reduce_det_scanvar(self, run, detector_key, scanvar_key, scanvar_bins_key): - """ - Reduce detector data by binning according to an arbitrary scan variable. - - This method bins the detector data based on a specified scan variable and its corresponding bins. - The result is stored in the `run` object under a new attribute. - - Parameters - ---------- - run : object - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data within the run object. - scanvar_key : str - The key corresponding to the scan variable indices. - scanvar_bins_key : str - The key corresponding to the scan variable bins. - - Returns - ------- - None - The reduced data is stored in the `run` object with the key formatted as `{detector_key}_scanvar_reduced`. - """ - - detector = getattr(run, detector_key) - - scanvar_indices = getattr(run, scanvar_key) # Shape: (4509,) - scanvar_bins = getattr(run, scanvar_bins_key) - - n_bins = len(scanvar_bins) # Number of bins - - # Initialize reduced_array with the correct shape (number of bins, 699, 50) - reduced_array = np.zeros( - (n_bins, detector.shape[1], detector.shape[2])) - - # Iterate over the images and accumulate them into reduced_array based on timing_indices - for i in range(detector.shape[0]): - np.add.at(reduced_array, (scanvar_indices[i],), detector[i]) - - # Store the reduced_array in the object, replace 'key_name' with the actual key - setattr(run, f"{detector_key}_scanvar_reduced", reduced_array) - - # Update status - run.update_status(f'Detector binned in time into key: {detector_key}_scanvar_reduced')
-
- - - -
-[docs] -class XASAnalysis(SpectroscopyAnalysis): - def __init__(self): - pass - -
-[docs] - def trim_ccm(self, run, threshold=120): - """ - Trim CCM values to remove bins with fewer shots than a specified threshold. - - Parameters - ---------- - run : object - The spectroscopy run instance. - threshold : int, optional - The minimum number of shots required to keep a CCM value (default is 120). - """ - - ccm_bins = getattr(run, 'ccm_bins', elist_center) - ccm_energies = getattr(run, 'ccm_energies', elist) - counts = np.bincount(bins) - trimmed_ccm = ccm_energies[counts[:-1] > 120] - self.make_ccm_axis(run, ccm_energies)
- - -
-[docs] - def make_ccm_axis(self, run, energies): - """ - Generate CCM bins and centers from given energy values. - - Parameters - ---------- - run : object - The spectroscopy run instance. - energies : array-like - Array of energy values to be used for creating CCM bins. - """ - elist = energies -# addon = (elist[-1] - elist[-2])/2 # add on energy -# elist2 = np.append(elist,elist[-1]+addon) # elist2 will be elist with dummy value at end -# elist_center = np.empty_like(elist2) -# for ii in np.arange(elist.shape[0]): -# if ii == 0: -# elist_center[ii] = elist2[ii] - (elist2[ii+1] - elist2[ii])/2 -# else: -# elist_center[ii] = elist2[ii] - (elist2[ii] - elist2[ii-1])/2 -# elist_center[-1] = elist2[-1] - addon = (elist[-1] - elist[-2])/2 - elist2 = np.append(elist, elist[-1]+addon) - elist_center = np.empty_like(elist) - - for ii in np.arange(elist_center.shape[0]): - if ii == elist_center.shape[0]: - elist_center[ii] = elist[-1]+addon - else: - elist_center[ii] = elist2[ii+1] - (elist2[ii+1] - elist2[ii])/2 - - setattr(run, 'ccm_bins', elist_center) - setattr(run, 'ccm_energies', elist)
- - -
-[docs] - def reduce_detector_ccm_temporal(self, run, detector_key, timing_bin_key_indices, ccm_bin_key_indices, average=True): - """ - Reduce detector data temporally and by CCM bins. - - Parameters - ---------- - run : object - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - timing_bin_key_indices : str - The key corresponding to the timing bin indices. - ccm_bin_key_indices : str - The key corresponding to the CCM bin indices. - average : bool, optional - Whether to average the reduced data (default is True). - """ - detector = getattr(run, detector_key) - # digitized indices from detector - timing_indices = getattr(run, timing_bin_key_indices) - # digitized indices from detector - ccm_indices = getattr(run, ccm_bin_key_indices) - reduced_array = np.zeros( - (np.shape(run.time_bins)[0]+1, np.shape(run.ccm_bins)[0])) - unique_indices = np.column_stack((timing_indices, ccm_indices)) - np.add.at(reduced_array, - (unique_indices[:, 0], unique_indices[:, 1]), detector) - reduced_array = reduced_array[:-1, :] - setattr(run, detector_key+'_time_energy_binned', reduced_array) - run.update_status('Detector %s binned in time into key: %s' % ( - detector_key, detector_key+'_time_energy_binned'))
- - -
-[docs] - def reduce_detector_ccm(self, run, detector_key, ccm_bin_key_indices, average=False, not_ccm=False): - """ - Reduce detector data by CCM bins. - - Parameters - ---------- - run : object - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - ccm_bin_key_indices : str - The key corresponding to the CCM bin indices. - average : bool, optional - Whether to average the reduced data (default is False). - not_ccm : bool, optional - Whether to indicate that CCM is not being used (default is False). - - """ - detector = getattr(run, detector_key) - - # digitized indices from detector - ccm_indices = getattr(run, ccm_bin_key_indices) - if not_ccm: - reduced_array = np.zeros(np.max(ccm_indices)+1) - else: - reduced_array = np.zeros(np.shape(run.ccm_bins)[0]) - np.add.at(reduced_array, ccm_indices, detector) - setattr(run, detector_key+'_energy_binned', reduced_array) - - run.update_status('Detector %s binned in energy into key: %s' % ( - detector_key, detector_key+'_energy_binned'))
- - -
-[docs] - def reduce_detector_temporal(self, run, detector_key, timing_bin_key_indices, average=False): - """ - Reduce detector data temporally. Specifically the 1d detector output for XAS data. - - Parameters - ---------- - run : object - The spectroscopy run instance. - detector_key : str - The key corresponding to the detector data. - timing_bin_key_indices : str - The key corresponding to the timing bin indices. - average : bool, optional - Whether to average the reduced data (default is False). - """ - detector = getattr(run, detector_key) - time_bins = run.time_bins - # digitized indices from detector - timing_indices = getattr(run, timing_bin_key_indices) - reduced_array = np.zeros(np.shape(time_bins)[0]+1) - np.add.at(reduced_array, timing_indices, detector) - setattr(run, detector_key+'_time_binned', reduced_array) - run.update_status('Detector %s binned in time into key: %s' % - (detector_key, detector_key+'_time_binned'))
- - -
-[docs] - def ccm_binning(self, run, ccm_bins_key, ccm_key='ccm'): - """ - Generate CCM bin indices from CCM data and bins. - - Parameters - ---------- - run : object - The spectroscopy run instance. - ccm_bins_key : str - The key corresponding to the CCM bins. - ccm_key : str, optional - The key corresponding to the CCM data (default is 'ccm'). - """ - ccm = getattr(run, ccm_key) - bins = getattr(run, ccm_bins_key) - run.ccm_bin_indices = np.digitize(ccm, bins) - run.update_status('Generated ccm bins from %f to %f in %d steps.' % ( - np.min(bins), np.max(bins), len(bins)))
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/XSpect/XSpect_Controller.html b/docs/_build/html/_modules/XSpect/XSpect_Controller.html deleted file mode 100644 index 89d5f17..0000000 --- a/docs/_build/html/_modules/XSpect/XSpect_Controller.html +++ /dev/null @@ -1,790 +0,0 @@ - - - - - - XSpect.XSpect_Controller — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for XSpect.XSpect_Controller

-
-[docs] -class BatchAnalysis: - def __init__(self, verbose=False): - self.verbose = verbose - self.status = [] - self.status_datetime = [] - self.filters = [] - self.keys = [] - self.friendly_names = [] - self.runs = [] - self.run_shots = {} - self.run_shot_ranges = {} - self.analyzed_runs = [] - -
-[docs] - def update_status(self, update): - self.status.append(update) - self.status_datetime.append( - datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - if self.verbose: - print(update)
- - -
-[docs] - def run_parser(self, run_array): - self.update_status("Parsing run array.") - run_string = ' '.join(run_array) - runs = [] - for run_range in run_string.split(): - if '-' in run_range: - start, end = map(int, run_range.split('-')) - runs.extend(range(start, end+1)) - else: - try: - runs.append(int(run_range)) - except ValueError: - raise ValueError(f"Invalid input: {run_range}") - - self.runs = runs
- - -
-[docs] - def add_filter(self, shot_type, filter_key, threshold): - self.update_status(f"Adding filter: Shot Type={shot_type}, Filter Key={filter_key}, Threshold={threshold}") - if shot_type not in ['xray', 'laser', 'simultaneous']: - raise ValueError( - 'Only options for shot type are xray, laser, or simultaneous.') - self.filters.append( - {'FilterType': shot_type, 'FilterKey': filter_key, 'FilterThreshold': threshold})
- - -
-[docs] - def set_key_aliases(self, keys=['tt/ttCorr', 'epics/lxt_ttc', 'enc/lasDelay', 'ipm4/sum', 'tt/AMPL', 'epix_2/ROI_0_area'], - names=['time_tool_correction', 'lxt_ttc', 'encoder', 'ipm', 'time_tool_ampl', 'epix']): - self.update_status("Setting key aliases.") - if 'epix' in names: - warnings.warn( - 'If you plan on using delayed key loading for the epix then please define key_epix and friendly_name_epix. And do not load it here for risk of OOM') - self.friendly_name_epix = ['epix'] - self.keys = keys - self.friendly_names = names
- - -
-[docs] - def primary_analysis_loop(self, experiment, verbose=False): - self.update_status(f"Starting primary analysis loop with experiment={experiment}, verbose={verbose}.") - analyzed_runs = [] - for run in self.runs: - analyzed_runs.append( - self.primary_analysis(experiment, run, verbose)) - self.analyzed_runs = analyzed_runs - self.update_status("Primary analysis loop completed.")
- - -
-[docs] - def primary_analysis_parallel_loop(self, cores, experiment, verbose=False): - self.update_status(f"Starting parallel analysis loop with cores={cores}, experiment={experiment}, verbose={verbose}.") - pool = Pool(processes=cores) - analyzed_runs = [] - - def callback(result): - analyzed_runs.append(result) - - with tqdm(total=len(self.runs), desc="Processing Runs", unit="Run") as pbar: - for analyzed_run in pool.imap(partial(self.primary_analysis, experiment=experiment, verbose=verbose), self.runs): - pbar.update(1) - analyzed_runs.append(analyzed_run) - - pool.close() - pool.join() - - analyzed_runs = [analyzed_run for analyzed_run in sorted( - analyzed_runs, key=lambda x: (x.run_number, x.end_index))] - self.analyzed_runs = analyzed_runs - self.update_status("Parallel analysis loop completed.")
- - -
-[docs] - def primary_analysis(self): - raise AttributeError( - 'The primary_analysis must be implemented by the child classes.')
- - -
-[docs] - def parse_run_shots(self, experiment, verbose=False): - self.update_status("Parsing run shots.") - run_shots_dict = {} - for run in self.runs: - f = spectroscopy_run( - experiment, run, verbose=verbose, end_index=self.end_index) - f.get_run_shot_properties() - run_shots_dict[run] = f.total_shots - self.run_shots = run_shots_dict - self.update_status("Run shots parsed.")
- - -
-[docs] - def break_into_shot_ranges(self, increment): - self.update_status( - f"Breaking into shot ranges with increment {increment}.") - run_shot_ranges_dict = {} - for run, total_shots in self.run_shots.items(): - run_shot_ranges = [] - min_index = 0 - if self.end_index is not None and self.end_index != -1: - total_shots = min(self.end_index, total_shots) - while min_index < total_shots: - max_index = min_index + increment - 1 if min_index + \ - increment - 1 < total_shots else total_shots - 1 - run_shot_ranges.append((min_index, max_index)) - min_index += increment - run_shot_ranges_dict[run] = run_shot_ranges - self.run_shot_ranges = run_shot_ranges_dict - self.update_status("Shot ranges broken.") - # Convert dictionary items to a list of tuples - - flat_list = [(run, (shot_range[0], shot_range[1])) for run, - shot_ranges in run_shot_ranges_dict.items() for shot_range in shot_ranges] - - result_array = np.array(flat_list, dtype=object) - self.run_shot_ranges = result_array
- - -
-[docs] - def primary_analysis_parallel_range(self, cores, experiment, increment, start_index=None, end_index=None, verbose=False, method=None): - - self.update_status("Starting parallel analysis with shot ranges.") - self.parse_run_shots(experiment, verbose) - self.break_into_shot_ranges(increment) - - analyzed_runs = [] - total_runs = len(self.run_shot_ranges) - - with Pool(processes=cores) as pool, tqdm(total=total_runs, desc="Processing", unit="Shot_Batch") as pbar: - run_shot_ranges = self.run_shot_ranges - - def callback(result): - nonlocal pbar - if isinstance(result, dict) and "error" in result: - self.update_status(f"Error in processing run {result['run']}: {result['error']}") - else: - analyzed_runs.append(result) - pbar.update(1) - - def error_callback(e): - self.update_status(f"Parallel processing error: {str(e)}") - - for run_shot in run_shot_ranges: - run, shot_ranges = run_shot - pool.apply_async(self.primary_analysis_range, - (experiment, run, shot_ranges, verbose, method), - callback=callback, - error_callback=error_callback) - - pool.close() - pool.join() - - self.analyzed_runs = analyzed_runs - analyzed_runs = [analyzed_run for analyzed_run in sorted( - analyzed_runs, key=lambda x: (x.run_number, x.end_index))] - self.analyzed_runs = analyzed_runs - self.update_status("Parallel analysis with shot ranges completed.")
-
- - - -
-[docs] -def analyze_single_run(args): - obj, experiment, run, shot_ranges, verbose = args - return obj.primary_analysis_range(experiment, run, shot_ranges, verbose)
- - - -
-[docs] -class XESBatchAnalysis(BatchAnalysis): - def __init__(self): - super().__init__() - self.xes_line = 'kbeta' - self.pixels_to_patch = [351, 352, 529, 530, 531] - self.crystal_detector_distance = 50.6 - self.crystal_d_space = 0.895 - self.crystal_radius = 250 - self.adu_cutoff = 3.0 - self.rois = [[0, None]] - self.mintime = -2.0 - self.maxtime = 10.0 - self.numpoints = 240 - self.time_bins = np.linspace( - self.mintime, self.maxtime, self.numpoints) - self.filters = [] - self.key_epix = ['epix_2/ROI_0_area'] - self.friendly_name_epix = ['epix'] - self.angle = 0.0 - self.end_index = -1 - self.start_index = 0 - self.transpose = False - self.lxt_key = 'lxt_ttc' - self.import_roi = None - -
-[docs] - def primary_analysis(self, experiment, run, verbose=False, start_index=None, end_index=None): - if end_index == None: - end_index = self.end_index - if start_index == None: - try: - start_index = self.start_index - except AttributeError: - start_index = 0 - self.time_bins = np.linspace( - self.mintime, self.maxtime, self.numpoints) - f = spectroscopy_run(experiment, run, verbose=verbose, - start_index=start_index, end_index=end_index) - f.load_run_keys(self.keys, self.friendly_names) - f.load_run_key_delayed(self.key_epix, self.friendly_name_epix) - f.get_run_shot_properties() - analysis = XESAnalysis() - analysis.reduce_detector_spatial( - f, 'epix', rois=self.rois, adu_cutoff=self.adu_cutoff) - analysis.filter_detector_adu(f, 'epix', adu_threshold=self.adu_cutoff) - analysis.union_shots(f, 'epix_ROI_1', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'epix_ROI_1', ['xray', 'laser']) - self.bins = np.linspace(self.mintime, self.maxtime, self.numpoints) - analysis.time_binning(f, self.bins, lxt_key=self.lxt_key) - analysis.union_shots(f, 'timing_bin_indices', [ - 'simultaneous', 'laser']) - analysis.separate_shots(f, 'timing_bin_indices', ['xray', 'laser']) - analysis.reduce_detector_temporal( - f, 'epix_ROI_1_simultaneous_laser', 'timing_bin_indices_simultaneous_laser', average=False) - analysis.reduce_detector_temporal( - f, 'epix_ROI_1_xray_not_laser', 'timing_bin_indices_xray_not_laser', average=False) - analysis.normalize_xes(f, 'epix_ROI_1_simultaneous_laser_time_binned') - analysis.normalize_xes(f, 'epix_ROI_1_xray_not_laser_time_binned') - if self.pixels_to_patch.any() != None: - analysis.pixels_to_patch = self.pixels_to_patch - f.close_h5() - analysis.make_energy_axis( - f, f.epix_ROI_1.shape[1], A=self.crystal_detector_distance, R=self.crystal_radius, d=self.crystal_d_space) - for fil in self.filters: - analysis.filter_shots( - f, fil['FilterType'], fil['FilterKey'], fil['FilterThreshold']) - - return f
-
- - - -
-[docs] -class XESBatchAnalysisRotation(XESBatchAnalysis): - def __init__(self): - super().__init__() - -
-[docs] - def primary_analysis_static_parallel_loop(self, cores, experiment, verbose=False): - self.update_status(f"Starting parallel analysis loop with cores={cores}, experiment={experiment}, verbose={verbose}.") - pool = Pool(processes=cores) - analyzed_runs = [] - - def callback(result): - analyzed_runs.append(result) - - with tqdm(total=len(self.runs), desc="Processing Runs", unit="Run") as pbar: - for analyzed_run in pool.imap(partial(self.primary_analysis_static, experiment=experiment, verbose=verbose), self.runs): - pbar.update(1) - analyzed_runs.append(analyzed_run) - - pool.close() - pool.join() - - analyzed_runs = [analyzed_run for analyzed_run in sorted( - analyzed_runs, key=lambda x: (x.run_number, x.end_index))] - self.analyzed_runs = analyzed_runs - self.update_status("Parallel analysis loop completed.")
- - -
-[docs] - def primary_analysis_static(self, run, experiment, verbose=False, start_index=None, end_index=None): - if end_index == None: - end_index = self.end_index - if start_index == None: - try: - start_index = self.start_index - except AttributeError: - start_index = 0 - f = spectroscopy_run(experiment, run, verbose=verbose, - start_index=start_index, end_index=end_index) - # f.load_run_keys(self.keys,self.friendly_names) - f.load_run_key_delayed( - self.key_epix, self.friendly_name_epix, rois=self.import_roi) - f.get_run_shot_properties() - analysis = XESAnalysis() - analysis.pixels_to_patch = self.pixels_to_patch - analysis.filter_detector_adu(f, 'epix', adu_threshold=self.adu_cutoff) - analysis.patch_pixels(f, 'epix', axis=1) - for fil in self.filters: - analysis.filter_shots( - f, fil['FilterType'], fil['FilterKey'], fil['FilterThreshold']) - analysis.reduce_detector_spatial( - f, 'epix', rois=self.rois, adu_cutoff=self.adu_cutoff) - analysis.reduce_detector_shots(f, 'epix_ROI_1') - keys_to_save = ['start_index', 'end_index', 'run_file', 'run_number', - 'verbose', 'status', 'status_datetime', 'epix_ROI_1_summed'] - f.purge_all_keys(keys_to_save) - return f
- - -
-[docs] - def primary_analysis(self, experiment, run, verbose=False, start_index=None, end_index=None): - if end_index == None: - end_index = self.end_index - if start_index == None: - try: - start_index = self.start_index - except AttributeError: - start_index = 0 - self.time_bins = np.linspace( - self.mintime, self.maxtime, self.numpoints) - self.end_index = end_index - self.start_index = start_index - f = spectroscopy_run(experiment, run, verbose=verbose, - start_index=start_index, end_index=end_index) - f.load_run_keys(self.keys, self.friendly_names) - f.load_run_key_delayed( - self.key_epix, self.friendly_name_epix, rois=self.import_roi) - f.get_run_shot_properties() - - analysis = XESAnalysis() - analysis.pixels_to_patch = self.pixels_to_patch - analysis.filter_detector_adu(f, 'epix', adu_threshold=self.adu_cutoff) - # analysis.patch_pixels(f,'epix',axis=1) - f.epix = rotate(f.epix, angle=self.angle, axes=[1, 2]) - for fil in self.filters: - analysis.filter_shots( - f, fil['FilterType'], fil['FilterKey'], fil['FilterThreshold']) - analysis.union_shots(f, 'epix', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'epix', ['xray', 'laser']) - self.bins = np.linspace(self.mintime, self.maxtime, self.numpoints) - analysis.time_binning(f, self.bins, lxt_key=self.lxt_key) - analysis.union_shots(f, 'timing_bin_indices', [ - 'simultaneous', 'laser']) - analysis.separate_shots(f, 'timing_bin_indices', ['xray', 'laser']) - analysis.reduce_detector_temporal( - f, 'epix_simultaneous_laser', 'timing_bin_indices_simultaneous_laser', average=False) - analysis.reduce_detector_temporal( - f, 'epix_xray_not_laser', 'timing_bin_indices_xray_not_laser', average=False) - analysis.reduce_detector_spatial( - f, 'epix_simultaneous_laser_time_binned', rois=self.rois, adu_cutoff=self.adu_cutoff) - analysis.reduce_detector_spatial( - f, 'epix_xray_not_laser_time_binned', rois=self.rois, adu_cutoff=self.adu_cutoff) - analysis.make_energy_axis( - f, f.epix_xray_not_laser_time_binned_ROI_1.shape[1], d=self.crystal_d_space, R=self.crystal_radius, A=self.crystal_detector_distance) - keys_to_save = ['start_index', 'end_index', 'run_file', 'run_number', 'verbose', 'status', - 'status_datetime', 'epix_xray_not_laser_time_binned_ROI_1', 'epix_simultaneous_laser_time_binned_ROI_1'] - f.purge_all_keys(keys_to_save) - analysis.make_energy_axis( - f, f.epix_xray_not_laser_time_binned_ROI_1.shape[1], d=self.crystal_d_space, R=self.crystal_radius, A=self.crystal_detector_distance) - return f
- - -
-[docs] - def primary_analysis_range(self, experiment, run, shot_ranges, verbose=False, method=None): - try: - if method is None: - method = self.primary_analysis - start, end = shot_ranges - return method(run=run, experiment=experiment, start_index=start, end_index=end, verbose=verbose) - except Exception as e: - # Return or log the exception with enough details - return {"error": str(e), "run": run, "shot_ranges": shot_ranges}
- - -
-[docs] - def hit_find(self, experiment, run, verbose=False, start_index=None, end_index=None): - if end_index == None: - end_index = self.end_index - if start_index == None: - try: - start_index = self.start_index - except AttributeError: - start_index = 0 - f = spectroscopy_run(experiment, run, verbose=verbose, - start_index=start_index, end_index=end_index) - f.load_run_keys(self.keys, self.friendly_names) - f.load_run_key_delayed(self.key_epix, self.friendly_name_epix) - f.get_run_shot_properties() - - analysis = XESAnalysis() - analysis.pixels_to_patch = self.pixels_to_patch - analysis.filter_detector_adu(f, 'epix', adu_threshold=self.adu_cutoff) - f.epix = np.nansum(np.nansum(f.epix, axis=1), axis=1) - for fil in self.filters: - analysis.filter_shots( - f, fil['FilterType'], fil['FilterKey'], fil['FilterThreshold']) - return f
-
- - - -
-[docs] -class XASBatchAnalysis(BatchAnalysis): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.mintime = -2.0 - self.maxtime = 10.0 - self.numpoints = 240 - self.minccm = 7.105 - self.maxccm = 7.135 - self.numpoints_ccm = 90 - self.time_bins = np.linspace( - self.mintime, self.maxtime, self.numpoints) - self.filters = [] - -
-[docs] - def primary_analysis(self, experiment, run, verbose=False): - f = spectroscopy_run(experiment, run, verbose=verbose) - f.get_run_shot_properties() - - f.load_run_keys(self.keys, self.friendly_names) - if self.scattering == True: - f.load_sum_run_scattering('epix10k2M/azav_azav') - f.ipm = f.scattering[:-1] - analysis = XASAnalysis() - try: - ccm_val = getattr(f, 'ccm_E_setpoint') - elist = np.unique(ccm_val) - except KeyError as e: - self.update_status('Key does not exist: %s' % e.args[0]) - elist = np.linspace(self.minccm, self.maxccm, self.numpoints_ccm) - analysis.make_ccm_axis(f, elist) - for fil in self.filters: - analysis.filter_shots( - f, fil['FilterType'], fil['FilterKey'], fil['FilterThreshold']) - analysis.union_shots(f, 'epix', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'epix', ['xray', 'laser']) - analysis.union_shots(f, 'ipm', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ipm', ['xray', 'laser']) - analysis.union_shots(f, 'ccm', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ccm', ['xray', 'laser']) - self.time_bins = np.linspace( - self.mintime, self.maxtime, self.numpoints) - analysis.time_binning(f, self.time_bins) - analysis.ccm_binning(f, 'ccm_bins', 'ccm') - analysis.union_shots(f, 'timing_bin_indices', [ - 'simultaneous', 'laser']) - analysis.separate_shots(f, 'timing_bin_indices', ['xray', 'laser']) - analysis.union_shots(f, 'ccm_bin_indices', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ccm_bin_indices', ['xray', 'laser']) - analysis.reduce_detector_ccm_temporal( - f, 'epix_simultaneous_laser', 'timing_bin_indices_simultaneous_laser', 'ccm_bin_indices_simultaneous_laser', average=True) - analysis.reduce_detector_ccm_temporal( - f, 'epix_xray_not_laser', 'timing_bin_indices_xray_not_laser', 'ccm_bin_indices_xray_not_laser', average=True) - analysis.reduce_detector_ccm_temporal( - f, 'ipm_simultaneous_laser', 'timing_bin_indices_simultaneous_laser', 'ccm_bin_indices_simultaneous_laser', average=True) - analysis.reduce_detector_ccm_temporal( - f, 'ipm_xray_not_laser', 'timing_bin_indices_xray_not_laser', 'ccm_bin_indices_xray_not_laser', average=True) - return f
-
- - - -
-[docs] -class XASBatchAnalysis_1D_ccm(BatchAnalysis): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.minccm = 7.105 - self.maxccm = 7.135 - self.numpoints_ccm = 100 - self.filters = [] - -
-[docs] - def primary_analysis(self, experiment, run, verbose=False): - f = spectroscopy_run(experiment, run, verbose=verbose) - f.get_run_shot_properties() - - f.load_run_keys(self.keys, self.friendly_names) - analysis = XASAnalysis() - try: - ccm_val = getattr(f, 'ccm_E_setpoint') - elist = np.unique(ccm_val) - except KeyError as e: - self.update_status('Key does not exist: %s' % e.args[0]) - elist = np.linspace(self.minccm, self.maxccm, self.numpoints_ccm) - analysis.make_ccm_axis(f, elist) - for fil in self.filters: - analysis.filter_shots( - f, fil['FilterType'], fil['FilterKey'], fil['FilterThreshold']) - analysis.union_shots(f, 'epix', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'epix', ['xray', 'laser']) - analysis.union_shots(f, 'ipm', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ipm', ['xray', 'laser']) - analysis.union_shots(f, 'ccm', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ccm', ['xray', 'laser']) -# self.time_bins=np.linspace(self.mintime,self.maxtime,self.numpoints) -# analysis.time_binning(f,self.time_bins) - analysis.ccm_binning(f, 'ccm_bins', 'ccm') -# analysis.union_shots(f,'timing_bin_indices',['simultaneous','laser']) -# analysis.separate_shots(f,'timing_bin_indices',['xray','laser']) - analysis.union_shots(f, 'ccm_bin_indices', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ccm_bin_indices', ['xray', 'laser']) - analysis.reduce_detector_ccm( - f, 'epix_simultaneous_laser', 'ccm_bin_indices_simultaneous_laser', average=False) - analysis.reduce_detector_ccm( - f, 'epix_xray_not_laser', 'ccm_bin_indices_xray_not_laser', average=False) - analysis.reduce_detector_ccm( - f, 'ipm_simultaneous_laser', 'ccm_bin_indices_simultaneous_laser', average=False) - analysis.reduce_detector_ccm( - f, 'ipm_xray_not_laser', 'ccm_bin_indices_xray_not_laser', average=False) - return f
-
- - - -
-[docs] -class XASBatchAnalysis_1D_time(BatchAnalysis): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.mintime = -2.0 - self.maxtime = 10.0 - self.numpoints = 240 - self.minccm = 7.105 - self.maxccm = 7.135 - self.numpoints_ccm = 90 - self.time_bins = np.linspace( - self.mintime, self.maxtime, self.numpoints) - self.filters = [] - -
-[docs] - def primary_analysis(self, experiment, run, verbose=False): - f = spectroscopy_run(experiment, run, verbose=verbose) - f.get_run_shot_properties() - - f.load_run_keys(self.keys, self.friendly_names) - analysis = XASAnalysis() -# try: -# ccm_val = getattr(f, 'ccm_E_setpoint') -# elist = np.unique(ccm_val) -# except KeyError as e: -# self.update_status('Key does not exist: %s' % e.args[0]) -# elist = np.linspace(self.minccm,self.maxccm,self.numpoints_ccm) -# analysis.make_ccm_axis(f,elist) - self.time_bins = np.linspace( - self.mintime, self.maxtime, self.numpoints) - analysis.time_binning(f, self.time_bins) - for fil in self.filters: - analysis.filter_shots( - f, fil['FilterType'], fil['FilterKey'], fil['FilterThreshold']) - analysis.union_shots(f, 'epix', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'epix', ['xray', 'laser']) - analysis.union_shots(f, 'ipm', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ipm', ['xray', 'laser']) -# analysis.union_shots(f,'ccm',['simultaneous','laser']) -# analysis.separate_shots(f,'ccm',['xray','laser']) - - -# analysis.ccm_binning(f,'ccm_bins','ccm') - analysis.union_shots(f, 'timing_bin_indices', [ - 'simultaneous', 'laser']) - analysis.separate_shots(f, 'timing_bin_indices', ['xray', 'laser']) -# analysis.union_shots(f,'ccm_bin_indices',['simultaneous','laser']) -# analysis.separate_shots(f,'ccm_bin_indices',['xray','laser']) - analysis.reduce_detector_temporal( - f, 'epix_simultaneous_laser', 'timing_bin_indices_simultaneous_laser', average=False) - analysis.reduce_detector_temporal( - f, 'epix_xray_not_laser', 'timing_bin_indices_xray_not_laser', average=False) - analysis.reduce_detector_temporal( - f, 'ipm_simultaneous_laser', 'timing_bin_indices_simultaneous_laser', average=False) - analysis.reduce_detector_temporal( - f, 'ipm_xray_not_laser', 'timing_bin_indices_xray_not_laser', average=False) - return f
-
- - - -
-[docs] -class ScanAnalysis_1D(BatchAnalysis): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - pass - -
-[docs] - def primary_analysis(self, experiment, run, verbose=False): - f = spectroscopy_run(experiment, run=run, verbose=True) - analysis = XASAnalysis() - f.get_run_shot_properties() - f.load_run_keys(self.keys, self.friendly_names) - # f.ccm_bins=f.ccm - analysis.bin_uniques(f, 'scan') - analysis.union_shots(f, 'epix', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'epix', ['xray', 'laser']) - analysis.union_shots(f, 'ipm', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ipm', ['xray', 'laser']) - analysis.union_shots(f, 'scan', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'scan', ['xray', 'laser']) - analysis.union_shots(f, 'scanvar_indices', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'scanvar_indices', ['xray', 'laser']) - analysis.reduce_detector_ccm( - f, 'epix_simultaneous_laser', 'scanvar_indices_simultaneous_laser', average=False, not_ccm=True) - analysis.reduce_detector_ccm( - f, 'epix_xray_not_laser', 'scanvar_indices_xray_not_laser', average=False, not_ccm=True) - analysis.reduce_detector_ccm( - f, 'ipm_simultaneous_laser', 'scanvar_indices_simultaneous_laser', average=False, not_ccm=True) - analysis.reduce_detector_ccm( - f, 'ipm_xray_not_laser', 'scanvar_indices_xray_not_laser', average=False, not_ccm=True) - return f
-
- - - -
-[docs] -class ScanAnalysis_1D_XES(BatchAnalysis): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - pass - -
-[docs] - def primary_analysis(self, experiment, run, verbose=False): - f = spectroscopy_run(experiment, run=run, verbose=True) - analysis = XESAnalysis() - f.get_run_shot_properties() - f.load_run_keys(self.keys, self.friendly_names) - # f.ccm_bins=f.ccm - analysis.bin_uniques(f, 'scan') - analysis.union_shots(f, 'epix', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'epix', ['xray', 'laser']) - analysis.union_shots(f, 'ipm', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'ipm', ['xray', 'laser']) - analysis.union_shots(f, 'scan', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'scan', ['xray', 'laser']) - analysis.union_shots(f, 'scanvar_indices', ['simultaneous', 'laser']) - analysis.separate_shots(f, 'scanvar_indices', ['xray', 'laser']) - - analysis.reduce_det_scanvar( - f, 'epix_simultaneous_laser', 'scanvar_indices_simultaneous_laser', 'scanvar_bins') - analysis.reduce_det_scanvar( - f, 'epix_xray_not_laser', 'scanvar_indices_xray_not_laser', 'scanvar_bins') - - return f
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/XSpect/XSpect_Diagnostics.html b/docs/_build/html/_modules/XSpect/XSpect_Diagnostics.html deleted file mode 100644 index 9219d6d..0000000 --- a/docs/_build/html/_modules/XSpect/XSpect_Diagnostics.html +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - XSpect.XSpect_Diagnostics — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for XSpect.XSpect_Diagnostics

-import numpy as np
-import matplotlib as plt
-
-width = 1.5
-length = 5
-
-plt.rcParams['figure.figsize'] = (7.5, 4.5)
-plt.rcParams['font.size'] = 12
-plt.rcParams['xtick.major.width'] = width
-plt.rcParams['ytick.major.width'] = width
-plt.rcParams['xtick.major.size'] = length
-plt.rcParams['ytick.major.size'] = length
-plt.rcParams['xtick.top'] = True
-plt.rcParams['ytick.right'] = True
-plt.rcParams['xtick.direction'] = 'in'
-plt.rcParams['ytick.direction'] = 'in'
-
-# from XSpect.XSpect_Analysis import *
-
-
-
-[docs] -class plotting: - def __init__(self): - pass - -
-[docs] - def hplot(self, data, thresholds, plt_title, leg_title, xlabel, yscale): - - thres = np.array(thresholds) - - xmin = 1.2*np.nanmin(data) - xmax = 1.2*np.nanmax(data) - binstep = (xmax - xmin)/500 - - fig, ax = plt.subplots(ncols=1) - - ax.hist(data, bins=np.arange(xmin, xmax, binstep)) - - if thres.size == 1: - ax.axvline(thres, color=np.array( - [255, 0, 0])/255, linewidth=1.5, label=thres) - elif thres.size > 1: - - for ii in range(thres.size): - ax.axvline(thres[ii], color=np.array( - [255, 0, 0])/255, linewidth=1.5, label=thres[ii]) - - ax.set_title(plt_title, fontsize=14, fontweight='bold') - ax.set_xlabel(xlabel) - ax.set_ylabel('Counts') - ax.legend(title=leg_title) - ax.set_yscale(yscale) - - for axis in ['top', 'bottom', 'left', 'right']: - ax.spines[axis].set_linewidth(width) - fig.tight_layout() - plt.show()
- - -
-[docs] - def roiview(self, data, thres, plt_type, energy_dispersive_axis='vert'): - - cl = (np.nanpercentile(data, 1), np.nanpercentile(data, 99)) - - if plt_type == 'xes': - fig, ax = plt.subplots(ncols=1, nrows=2, figsize=(8, 8)) - p1 = ax[0].imshow(data, clim=cl) - ax[0].set_title('XES ROI', fontsize=14, fontweight='bold') - for lim in thres: - if energy_dispersive_axis == 'horiz' or energy_dispersive_axis == 'horizontal': - if thres[lim]: - roisum = np.nansum( - data[thres[lim][0]:thres[lim][1], :], axis=1) - p2 = ax[1].plot(roisum, linewidth=1.5, label=lim) - for ii in range(len(thres[lim])): - ax[0].axhline(thres[lim][ii], color='red', linewidth=1.5, - label=lim + ': {}'.format(thres[lim][ii])) - else: - if thres[lim]: - roisum = np.nansum( - data[:, thres[lim][0]:thres[lim][1]], axis=1) - p2 = ax[1].plot(roisum, linewidth=1.5, label=lim) - for ii in range(len(thres[lim])): - ax[0].axvline(thres[lim][ii], color='red', linewidth=1.5, - label=lim + ': {}'.format(thres[lim][ii])) - ax[1].set_title('ROI Projections', fontsize=14, fontweight='bold') - ax[1].set_xlabel('Pixel') - ax[1].set_ylabel('Summed Intensity') - ax[1].set_xlim([0, data.shape[0]]) - ax[1].legend() - cb = fig.colorbar(p1, ax=ax[0]) - - for plot in ax: - for axis in ['top', 'bottom', 'left', 'right']: - plot.spines[axis].set_linewidth(width) - - elif plt_type == 'xas': - fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(4, 4)) - p1 = ax.imshow(data, clim=cl) - ax.set_title('XAS ROI', fontsize=14, fontweight='bold') - for lim in thres: - if thres[lim] and lim == 'horiz': - for ii in range(len(thres[lim])): - ax.axvline(thres[lim][ii], color='red', linewidth=1.5, - label=lim + ': {}'.format(thres[lim][ii])) - elif thres[lim] and lim == 'vert': - for ii in range(len(thres[lim])): - ax.axhline(thres[lim][ii], color='red', linewidth=1.5, - label=lim + ': {}'.format(thres[lim][ii])) - cb = fig.colorbar(p1, ax=ax) - - for axis in ['top', 'bottom', 'left', 'right']: - ax.spines[axis].set_linewidth(width) - - # cb.ax.get_children()[7] is colorbar spine - cb.ax.get_children()[7].set_linewidth(width) - fig.tight_layout() - plt.show()
-
- - - -
-[docs] -class diagnostics(plotting): - def __init__(self, run, exp, keys, friendly_names): - - # when generating a diagnostics object, run no., exp no., keys, and friendly names are passed to the object - - self.run = run - self.exp = exp - self.keys = keys - self.friendly_names = friendly_names - - fpath = '/sdf/data/lcls/ds/{}/{}/hdf5/smalldata/'.format( - self.exp[:3], self.exp) - f = fpath + '{}_Run{:04d}.h5'.format(self.exp, self.run) - self.h5 = h5py.File(f) - print('Run {} imported'.format(self.run)) - - # generate a dictionary for the supplied keys/friendly names - - self.datadict = {} - for key, name in zip(keys, friendly_names): - self.datadict[name] = key - -
-[docs] - def load_run_keys(self): - - # reads keys from h5 file and stores as arrays in self with the friendly key name - - try: - getattr(self, 'h5') - except AttributeError: - print('Error: must run importruns() function first') - return - - with self.h5 as fh: - for key, name in zip(self.keys, self.friendly_names): - try: - setattr(self, name, fh[key][:]) - except KeyError as e: - print('Key does not exist: %s' % e)
- - -
-[docs] - def adu_histogram(self, nshots, thresholds, ROIopt=False, energy_dispersive_axis='vert'): - - # generate linearized array of pixel intensities (ADU) over first nshots of events and plot histogram - - pt = 'Pixel Intensity on ePix100 Run {} ({} shots)'.format( - self.run, nshots) - lt = 'ADU Thresholds' - xl = 'Pixel Intensity (keV)' - ys = 'log' - - if ROIopt: - if hasattr(self, 'xes_roi_limits'): - print('Calculating histograms for XES ROIs:') - thres = self.xes_roi_limits - for lim in thres: - if thres[lim]: - if energy_dispersive_axis == 'horiz' or energy_dispersive_axis == 'horizontal': - data2plot = self.h5[self.datadict['epix']][0:nshots, - thres[lim][0]:thres[lim][1], :].ravel() - else: - data2plot = self.h5[self.datadict['epix']][0:nshots, - :, thres[lim][0]:thres[lim][1]].ravel() - - pt = 'Pixel Intensity in {} ROI ({} shots)'.format( - lim, nshots) - self.hplot(data2plot, thresholds, pt, lt, xl, ys) - - elif hasattr(self, 'xas_roi_limits'): - print('Calculating histograms for XAS ROI:') - thres = self.xas_roi_limits - data2plot = self.h5[self.datadict['epix']][0:nshots, thres['vert'] - [0]:thres['vert'][1], thres['horiz'][0]:thres['horiz'][1]].ravel() - pt = 'Pixel Intensity in XAS ROI ({} shots)'.format(nshots) - self.hplot(data2plot, thresholds, pt, lt, xl, ys) - - else: - print( - 'Error: no ROIs have been set, run function xes_ROI or xas_ROI with option setrois = True)') - return - - else: - data2plot = self.h5[self.datadict['epix']][0:nshots, :, :].ravel() - pt = 'Pixel Intensity on ePix100 Run {} ({} shots)'.format( - self.run, nshots) - self.hplot(data2plot, thresholds, pt, lt, xl, ys)
- - -
-[docs] - def ipm_histogram(self, thresholds): - - # plot histogram of chosen IPM values (measure of incoming X-ray intensity) over all events - - data2plot = self.h5[self.datadict['ipm']][:] - pt = '{} Histogram'.format(self.datadict['ipm']) - lt = 'IPM Filters' - xl = self.datadict['ipm'] - ys = 'linear' - - self.hplot(data2plot, thresholds, pt, lt, xl, ys)
- - -
-[docs] - def ttAMPL_histogram(self, thresholds): - - # plot histogram of time tool amplitude over all events - - data2plot = self.h5[self.datadict['time_tool_ampl']][:] - pt = '{} Histogram'.format(self.datadict['time_tool_ampl']) - lt = 'ttAMPL Filters' - xl = self.datadict['time_tool_ampl'] - ys = 'log' - - self.hplot(data2plot, thresholds, pt, lt, xl, ys)
- - -
-[docs] - def xes_ROI(self, nshots, kb_limits=[], ka_limits=[], setrois=False, energy_dispersive_axis='vert', angle=0): - - # plots summed spectroscopy detector image over first nshots events as well as any ROI limits provided - - roi_limits = {} - roi_limits['Ka'] = ka_limits - roi_limits['Kb'] = kb_limits - - if setrois: - setattr(self, 'xes_roi_limits', roi_limits) - - data2plot = np.nansum( - self.h5[self.datadict['epix']][0:nshots, :, :], axis=0) - data2plot_rot = rotate(data2plot, angle, axes=[0, 1]) - - ptype = 'xes' - - self.roiview(data2plot, roi_limits, ptype, - energy_dispersive_axis=energy_dispersive_axis)
- - -
-[docs] - def xas_ROI(self, nshots, horiz_limits=[], vert_limits=[], setrois=False): - - # plots summed spectroscopy detector image over first nshots events as well as any ROI limits provided - - roi_limits = {} - roi_limits['horiz'] = horiz_limits - roi_limits['vert'] = vert_limits - - if setrois: - setattr(self, 'xas_roi_limits', roi_limits) - - data2plot = np.nansum( - self.h5[self.datadict['epix']][0:nshots, :, :], axis=0) - - ptype = 'xas' - - self.roiview(data2plot, roi_limits, ptype)
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/XSpect/XSpect_PostProcessing.html b/docs/_build/html/_modules/XSpect/XSpect_PostProcessing.html deleted file mode 100644 index 0aaaac4..0000000 --- a/docs/_build/html/_modules/XSpect/XSpect_PostProcessing.html +++ /dev/null @@ -1,565 +0,0 @@ - - - - - - XSpect.XSpect_PostProcessing — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for XSpect.XSpect_PostProcessing

-import numpy as np
-import matplotlib as plt
-
-width = 1.5
-length = 5
-
-plt.rcParams['figure.figsize'] = (7.5, 4.5)
-plt.rcParams['font.size'] = 12
-plt.rcParams['xtick.major.width'] = width
-plt.rcParams['ytick.major.width'] = width
-plt.rcParams['xtick.major.size'] = length
-plt.rcParams['ytick.major.size'] = length
-plt.rcParams['xtick.top'] = True
-plt.rcParams['ytick.right'] = True
-plt.rcParams['xtick.direction'] = 'in'
-plt.rcParams['ytick.direction'] = 'in'
-
-
-
-[docs] -class plotting: - def __init__(self): - pass
- - - -
-[docs] -class analysis_functions: - def __init__(self): - pass - -
-[docs] - def expfunc(self, x, k, amp=[], x0=0): - # returns a vector (or matrix of column vectors, depending on the length of k) containing an exponential decay evalutated over x values x with rate constant(s) k - if not isinstance(k, list): - k = [k] - k = np.abs(k) # always takes positive values and enforces exp decay - f = np.empty((len(x), len(k))) - for i in range(len(k)): - f[:, i] = np.exp(-k[i]*(x-x0)) - return f
- - -
-[docs] - def expfunc_heaviside(self, x, k, amp=[], x0=0): - # returns a vector (or matrix of column vectors) containing an exponential decay with rate constant(s) k multiplied by the heaviside step function (x < 0, y = 0; x = 0, y = 0.5; x > 0, y = 1) evaluated over x - if isinstance(k, list): - k = np.array(k) - if isinstance(k, float) or isinstance(k, int): - k = np.array([k]) - - if isinstance(amp, list): - amp = np.array(amp) - if isinstance(amp, float) or isinstance(amp, int): - amp = np.array([amp]) - if not amp.size > 0: - amp = np.ones_like(np.array(k)) - - k = np.abs(k) - f = np.empty((len(x), len(k))) - for i in range(len(k)): - # h(x) = 0 (x <= 0), h(x) = 1 (x > 0) - f[:, i] = amp[i]*np.exp(-k[i]*(x-x0))*np.heaviside((x-x0), 0.5) - return f
- - -
-[docs] - def gaussfunc(self, x, center, sigma): - return np.exp((-(x - center)**2)/(2*sigma**2))
- - -
-[docs] - def gaussfunc_norm(self, x, center, sigma): - return (1/(sigma*np.sqrt(2*np.pi)))*np.exp((-(x - center)**2)/(2*sigma**2))
- - -
-[docs] - def irfconv(self, x, k, center, sigma, amp=[]): - - # numerical convolution between heaviside*exponential and gaussian IRF - - if isinstance(k, list): - k = np.array(k) - if isinstance(k, float) or isinstance(k, int): - k = np.array([k]) - - if isinstance(amp, list): - amp = np.array(amp) - if isinstance(amp, float) or isinstance(amp, int): - amp = np.array([amp]) - if not amp.size > 0: - amp = np.ones_like(np.array(k)) - - dx = min(np.diff(x)) - conv_xgrid = np.arange(min(x), max(x)*1.2, dx/10) - cdx = min(np.diff(conv_xgrid)) - - expmat = self.expfunc_heaviside(conv_xgrid, k, amp) - gaussmat = np.empty_like(expmat) - - # for fftconvolve, we need expmat and gaussmat to have same dimensions - - if sigma == 0: - # if linewidth is given as 0, then convolve with a delta fxn (normalization factor cdx set to 1) - for i in range(len(k)): - gaussmat[:, i] = scipy.signal.unit_impulse( - gaussmat[:, i].shape, idx=np.argmin(abs(conv_xgrid-center))) - cdx = 1 - else: - for i in range(len(k)): - gaussmat[:, i] = self.gaussfunc_norm(conv_xgrid, center, sigma) - # elif sigma < 0: - # print('Error: cannot have negative width parameter') - # print('Using absolute value of given input') - # for i in range(len(k)): - # gaussmat[:,i] = gaussfunc_norm(conv_xgrid, center, np.abs(sigma)) - - pre0 = conv_xgrid[conv_xgrid < 0].size - - conv_vec = scipy.signal.fftconvolve(expmat, gaussmat, axes=0) - C = conv_vec[pre0:(len(conv_xgrid)+pre0)]*cdx - - C_interp = scipy.interpolate.interp1d(conv_xgrid, C, axis=0)(x) - - return C_interp
- - -
-[docs] - def irfconv_ana(self, x, k, center, sigma, amp=[]): - - # returns analytical convolution between heaviside*exponential and gaussian - - if isinstance(k, list): - k = np.array(k) - if isinstance(k, float) or isinstance(k, int): - k = np.array([k]) - - if isinstance(amp, list): - amp = np.array(amp) - if isinstance(amp, float) or isinstance(amp, int): - amp = np.array([amp]) - if not amp.size > 0: - amp = np.ones_like(np.array(k)) - - k = np.abs(k) - f = np.empty((len(x), len(k))) - - if sigma > 0: - for i in range(len(k)): - a1 = amp[i]/2 - a2 = np.exp(-k[i]*(x-center)) - a3 = np.exp(0.5*(k[i]*sigma)**2) - a4 = 1 + \ - scipy.special.erf( - (x - center - k[i]*(sigma**2))/(np.sqrt(2)*sigma)) - f[:, i] = a1*a2*a3*a4 - elif sigma == 0: - f = self.expfunc_heaviside(x, k, amp=amp, x0=center) - else: - print('Error: IRF linewidth must be positive') - return - return f
- - -
-[docs] - def kmatsolver(self, kmatrix, x, k, X0, center, sigma, irf_option='numerical', printopt=True): - - # Berberan-Santos, M. N. and Martinho, J. M. G. The integration of kinetic rate equations by matrix methods. J. Chem. Ed. 1990, 67, 375 - - # For solving a system of rate equations that are purely composed of unimolecular (first-order) steps, the analytical solution always exists and can be found using the rate constant (k) matrix and its eigenvectors/values - - # dX1/dt = k11*X1 + k12*X2 - # dX2/dt = k21*X1 + k22*X2 - ## - # Recast in matrix form: - ## - # [dX1/dt] [k11 k12] [X1] - # [dX2/dt] = [k21 k22] [X2] - ## - - if isinstance(k, list): - k = np.array(k) - if isinstance(k, float) or isinstance(k, int): - k = np.array([k]) - - # kmatrix is easiest to define as a lambda function that accepts a list of rate constants and uses that to populate an array (kmat) - - kmat = kmatrix(k) - - if kmat.shape == (1,): - kmat = np.expand_dims(kmat, axis=1) - - if printopt: - print('Printing k Matrix:') - print(kmat) - - # solve eigenvalues/eigenvectors of k matrix; the eigenvalues are the rates and the eigenvectors inform what linear combination of exponentials leads to the solution of the system of equations - - eigval, eigvec = np.linalg.eig(kmat) - - # a is a set of constants dependent on the eigenvectors and initial amplitudes - - if len(X0) == 1: - a = np.linalg.inv(eigvec)*X0 - elif len(X0) > 1: - a = np.linalg.inv(eigvec)@X0 - - # evaluate exponential fxns (with or without gaussian IRF convolution) and store as column vectors in v - - if irf_option == 'numerical': - v = self.irfconv(x, eigval, center, sigma) - elif irf_option == 'analytical': - v = self.irfconv_ana(x, eigval, center, sigma) - elif irf_option == 'none': - v = self.expfunc_heaviside(x, eigval) - else: - print('irf_option can only take "numerical", "analytical", or "none"') - - # Xt is the final concentration matrix - - Xt = np.transpose(eigvec@np.transpose(a*v)) - - return Xt
-
- - - -
-[docs] -class post_analysis(analysis_functions): - def __init__(self): - pass - -
-[docs] - def svdplot(self, xval, yval, data, ncomp): - U, S, V = np.linalg.svd(data) - - fig, ax = plt.subplots(ncols=3, nrows=1, figsize=(8, 4)) - - offsetU = 0 - offsetbaseU = np.max(np.abs(U[:, 0:ncomp])) - offsetV = 0 - offsetbaseV = np.max(np.abs(V[0:ncomp, :])) - offsetlistU = [] - offsetlistV = [] - SVDindex = [] - - for ii in range(ncomp): - SVDindex.append(ii+1) - ax[0].plot(xval, U[:, ii] + offsetU, linewidth=2) - ax[1].scatter(SVDindex[ii], S[ii]) - ax[2].plot(yval, V[ii, :] + offsetV, linewidth=2) - offsetlistU.append(offsetU) - offsetlistV.append(offsetV) - offsetU -= offsetbaseU - offsetV -= offsetbaseV - - width = 1.5 - - for plot in ax: - for axis in ['top', 'bottom', 'left', 'right']: - plot.spines[axis].set_linewidth(width) - - ax[0].set_yticks(offsetlistU) - ax[0].set_yticklabels(SVDindex) - ax[0].set_title('Left Singular Vectors') - ax[0].set_xlim([min(xval), max(xval)]) - ax[1].set_yscale('log') - ax[1].set_xlim([min(SVDindex)-1, max(SVDindex)+1]) - ax[1].set_xticks(SVDindex) - ax[1].set_xticklabels(SVDindex) - ax[1].set_title('Scree Plot') - ax[2].set_yticks(offsetlistV) - ax[2].set_yticklabels(SVDindex) - ax[2].set_title('Right Singular Vectors') - ax[2].set_xlim([min(yval), max(yval)])
- - -
-[docs] - def svdreconstruct(self, data, ncomp): - U, S, V = np.linalg.svd(data) - - data_reconstruct = U[:, 0:ncomp]@np.diag(S[0:ncomp])@V[0:ncomp, :] - - print('SVD Reconstruction performed with {} components'.format(ncomp)) - - return data_reconstruct
- - -
-[docs] - def parse_theta(self, k=[], center=[], sigma=[], amplitudes=[]): - theta_parser = {'k': k, 'center': center, - 'sigma': sigma, 'amplitudes': amplitudes} - - return theta_parser
- - -
-[docs] - def construct_theta(self, theta_parser): - theta = [] - for count, key in enumerate(list(theta_parser.keys())): - theta = theta + theta_parser[key] - return np.array(theta)
- - -
-[docs] - def read_theta(self, theta, theta_parser): - - k_guess = [] - center_guess = [] - sigma_guess = [] - amplitude_guess = [] - - nk = len(theta_parser['k']) - ncenter = len(theta_parser['center']) - nsigma = len(theta_parser['sigma']) - namplitudes = len(theta_parser['amplitudes']) - - k_guess = theta[0:nk] - center_guess = theta[nk:nk+ncenter] - sigma_guess = theta[nk+ncenter:nk+ncenter+nsigma] - amplitudes_guess = theta[nk+ncenter + - nsigma:nk+ncenter+nsigma+namplitudes] - - parsed = self.parse_theta( - k=k_guess, center=center_guess, sigma=sigma_guess, amplitudes=amplitudes_guess) - - return parsed
- - -
-[docs] - def varproj(self, kmatrix, x, k, X0, center, sigma, data): - C = self.kmatsolver(kmatrix, x, k, X0, center, sigma, printopt=False) - - C_inv = np.linalg.pinv(C) - - Et = C_inv@data - - SimA = C@Et - - return C, np.transpose(Et), SimA
- - -
-[docs] - def targetobjective(self, theta, x, kmatrix, X0, theta_parser, data): - theta_dict = self.read_theta(theta, theta_parser) - C_guess, DAS, SimA = self.varproj( - kmatrix, x, theta_dict['k'], X0, theta_dict['center'], theta_dict['sigma'], data) - - res = data - SimA - - resl = res.ravel() - - return resl
- - -
-[docs] - def targetanalysis_run(self, data, x, kmatrix, k_in, center_in, sigma_in, X0_in, y=[], bounds_dict=None): - - theta_dict = self.parse_theta(k=k_in, center=center_in, sigma=sigma_in) - theta_in = self.construct_theta(theta_dict) - - if bounds_dict == None: - # set default parameter bounds - bounds_dict = {'lb': {}, 'ub': {}} - - for count, key in enumerate(list(theta_dict.keys())): - bounds_dict['lb'][key] = [] - bounds_dict['ub'][key] = [] - if key == 'k': - default_bound = [0, np.inf] - elif key == 'center': - default_bound = [-np.inf, np.inf] - elif key == 'sigma': - default_bound = [0, np.inf] - elif key == 'amplitudes': - default_bound = [-np.inf, np.inf] - for i in range(len(theta_dict[key])): - bounds_dict['lb'][key].append(default_bound[0]) - bounds_dict['ub'][key].append(default_bound[1]) - - lower = [] - upper = [] - for count, key in enumerate(list(bounds_dict['lb'].keys())): - lower += bounds_dict['lb'][key] - upper += bounds_dict['ub'][key] - - constraints = (lower, upper) - - res_lsq = scipy.optimize.least_squares(self.targetobjective, theta_in, args=( - x, kmatrix, X0_in, theta_dict, data), method='trf', bounds=constraints) - - theta_out = self.read_theta(res_lsq.x, theta_dict) - if 'k' in theta_out.keys(): - theta_out['k'][::-1].sort() - - C_fit, E_fit, A_fit = self.varproj( - kmatrix, x, theta_out['k'], X0_in, theta_out['center'], theta_out['sigma'], data) - - print('Fit Parameters:') - print(theta_out) - print('') - print('Cost:') - print(res_lsq.cost) - - fig, ax = plt.subplots(ncols=2, nrows=1) - - ax[0].plot(x, C_fit) - ax[0].set_title('Concentration') - ax[0].set_xlabel('Time') - ax[0].set_ylabel('Amplitude') - ax[0].set_xlim((min(x), max(x))) - - if isinstance(y, np.ndarray): - ax[1].plot(y, E_fit) - ax[1].set_xlabel('') - ax[1].set_xlim((min(y), max(y))) - elif not y: - ax[1].plot(E_fit) - ax[1].set_title('EAS') - # ax[1].set_ylabel('Amplitude') - - fig.suptitle('Target Analysis Results', fontsize=16) - - for plot in ax: - for axis in ['top', 'bottom', 'left', 'right']: - plot.spines[axis].set_linewidth(width) - - # calculate standard errors (se) and 95% confidence intervals (ci) from jacobian - J = res_lsq.jac - res = res_lsq.fun - n = res.shape[0] - p = J.shape[1] - v = n - p - rmse = np.linalg.norm(res, ord=2)/np.sqrt(v) - cov = np.linalg.inv(J.T@J)*rmse**2 - se = np.sqrt(np.diag(cov)) - setattr(res_lsq, 'se', se) - - a = 0.05 - delta = se*scipy.stats.t.ppf(1 - a/2, v) - ci = np.array([res_lsq.x-delta, res_lsq.x+delta]) - setattr(res_lsq, 'ci', ci) - - return res_lsq, C_fit, E_fit
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/XSpect/XSpect_Visualization.html b/docs/_build/html/_modules/XSpect/XSpect_Visualization.html deleted file mode 100644 index 35d0a0c..0000000 --- a/docs/_build/html/_modules/XSpect/XSpect_Visualization.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - - - XSpect.XSpect_Visualization — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for XSpect.XSpect_Visualization

-import numpy as np
-import matplotlib as plt
-
-
-[docs] -class SpectroscopyVisualization: - def __init__(self): - pass - -
-[docs] - def plot_2d_spectrum(self, run, detector_key): - spectrum = getattr(run, detector_key) - vmin, vmax = np.percentile(spectrum, [1, 99]) - fig, ax = plt.subplots(1, 1) - im = ax.imshow(spectrum, vmin=vmin, vmax=vmax, - origin='lower', aspect='auto')
- - -
-[docs] - def plot_2d_difference_spectrum(self, run, detector_keys): - spectrum_1 = getattr(run, detector_keys[0]) - spectrum_2 = getattr(run, detector_keys[1]) - spectrum = spectrum_1-spectrum_2 - vmin, vmax = np.percentile(spectrum, [1, 99]) - fig, ax = plt.subplots(1, 1) - im = ax.imshow(spectrum, vmin=vmin, vmax=vmax, - origin='lower', aspect='auto') - run.difference_spectrum = spectrum
-
- - - -
-[docs] -class XESVisualization(SpectroscopyVisualization): - def __init__(self): - self.vmin = -0.1 - self.vmax = 0.1 - pass - -
-[docs] - def plot_1d_XES(self, run, detector_key, target_key, low=-np.inf, high=np.inf, axis=0): - target = getattr(run, target_key) - intensities = getattr(run, detector_key) - if hasattr(run, run.xes_line+'_energy'): - idxlow = np.argmin(np.abs(target-low)) - idxhigh = np.argmin(np.abs(target-high)) - cut = np.nanmean(intensities[idxlow:idxhigh, :], axis=axis) - plt.plot(getattr(run, run.xes_line+'_energy'), cut) - run.current_cut = cut - else: - raise ValueError('There is no energy axis in this object')
- - -
-[docs] - def combine_spectra(self, xes_analysis, xes_key, xes_laser_key): - xes = getattr(xes_analysis.analyzed_runs[0], xes_key) - xes_laser = getattr(xes_analysis.analyzed_runs[0], xes_laser_key) - summed_laser_off = np.zeros_like(xes) - summed_laser_on = np.zeros_like(xes_laser) - for run in xes_analysis.analyzed_runs: - summed_laser_on += getattr(run, xes_laser_key) - summed_laser_off += getattr(run, xes_key) - self.summed_laser_on = summed_laser_on - self.summed_laser_off = summed_laser_off - analysis = XESAnalysis() - analysis.normalize_xes(self, 'summed_laser_on') - analysis.normalize_xes(self, 'summed_laser_off') - xes_analysis.summed_laser_on_normalized = self.summed_laser_on_normalized - xes_analysis.summed_laser_off_normalized = self.summed_laser_off_normalized
- - -
-[docs] - def combine_static_spectra(self, xes_analysis, xes_key): - xes = getattr(xes_analysis.analyzed_runs[0], xes_key) - summed_laser_off = np.zeros_like(xes) - for run in xes_analysis.analyzed_runs: - summed_laser_off += getattr(run, xes_key) - self.summed_xes = summed_laser_off
- - -
-[docs] - def plot_2d_difference_spectrum(self, xes_analysis): - laser_on_spectrum = xes_analysis.summed_laser_on_normalized - laser_off_spectrum = xes_analysis.summed_laser_off_normalized - difference_spectrum = laser_on_spectrum-laser_off_spectrum - try: - energy = xes_analysis.analyzed_runs[0].kbeta_energy - except: - energy = np.linspace(0, np.shape(laser_on_spectrum), 1) - # vmin, vmax = np.percentile(difference_spectrum, [0,99]) - plt.figure(dpi=100) - plt.imshow(difference_spectrum.T, cmap='RdBu', vmin=self.vmin, vmax=self.vmax, origin='lower', - aspect='auto', extent=[xes_analysis.mintime, xes_analysis.maxtime, energy[0], energy[-1]]) - plt.colorbar() - plt.xlabel('Time (ps)') - plt.ylabel('Energy (keV)') - setattr(xes_analysis, 'difference_spectrum', difference_spectrum)
-
- - - -
-[docs] -class XASVisualization(SpectroscopyVisualization): - def __init__(self): - self.vmin = -0.1 - self.vmax = 0.1 - pass - -
-[docs] - def plot_XAS(self, run, detector_key, ccm_key): - det = getattr(run, detector_key) - ccm = getattr(run, ccm_key) - plt.plot(ccm, det)
- - -
-[docs] - def combine_spectra(self, xas_analysis, xas_laser_key, xas_key, norm_laser_key, norm_key, interpolate=False): - xas = getattr(xas_analysis.analyzed_runs[0], xas_key) - xas_laser = getattr(xas_analysis.analyzed_runs[0], xas_laser_key) - norm = getattr(xas_analysis.analyzed_runs[0], norm_key) - norm_laser = getattr(xas_analysis.analyzed_runs[0], norm_laser_key) - - try: - ccm_bins = getattr(xas_analysis.analyzed_runs[0], 'ccm_energies') - setattr(xas_analysis, 'ccm_bins', ccm_bins) - except: - pass - - summed_laser_on = np.zeros_like(xas_laser) - summed_laser_off = np.zeros_like(xas) - summed_norm_on = np.zeros_like(norm_laser) - summed_norm_off = np.zeros_like(norm) - - for idx, run in enumerate(xas_analysis.analyzed_runs): - if idx > 0 and interpolate: - ccm = getattr(run, 'ccm_energies') - # assuming time axis is the first dimension - time = np.arange(xas_laser.shape[0]) - - interp_laser_on = interp2d(ccm, time, getattr( - run, xas_laser_key), fill_value=0, bounds_error=False) - interp_laser_off = interp2d(ccm, time, getattr( - run, xas_key), fill_value=0, bounds_error=False) - interp_norm_on = interp2d(ccm, time, getattr( - run, norm_laser_key), fill_value=0, bounds_error=False) - interp_norm_off = interp2d(ccm, time, getattr( - run, norm_key), fill_value=0, bounds_error=False) - - for t in time: - summed_laser_on[t, :] += interp_laser_on(ccm_bins, t) - summed_laser_off[t, :] += interp_laser_off(ccm_bins, t) - summed_norm_on[t, :] += interp_norm_on(ccm_bins, t) - summed_norm_off[t, :] += interp_norm_off(ccm_bins, t) - else: - summed_laser_on += np.array(getattr(run, xas_laser_key)) - summed_laser_off += np.array(getattr(run, xas_key)) - summed_norm_on += np.array(getattr(run, norm_laser_key)) - summed_norm_off += np.array(getattr(run, norm_key)) - - xas_analysis.summed_laser_on = summed_laser_on - xas_analysis.summed_laser_off = summed_laser_off - xas_analysis.summed_norm_on = summed_norm_on - xas_analysis.summed_norm_off = summed_norm_off
- - -
-[docs] - def plot_2d_difference_spectrum(self, xas_analysis, vmin=None, vmax=None): - laser_on_spectrum = xas_analysis.summed_laser_on/xas_analysis.summed_norm_on - laser_off_spectrum = np.divide(np.nansum( - xas_analysis.summed_laser_off, axis=0), np.nansum(xas_analysis.summed_norm_off, axis=0)) - difference_spectrum = laser_on_spectrum-laser_off_spectrum - setattr(xas_analysis, 'difference_spectrum', difference_spectrum) -# vmin, vmax = np.nanpercentile(difference_spectrum, [0,99]) - plt.figure(dpi=100) -# plt.imshow(difference_spectrum.T, cmap='RdBu', vmin=self.vmin, vmax=self.vmax, origin='lower',aspect='auto',extent=[xas_analysis.mintime,xas_analysis.maxtime,xas_analysis.minccm,xas_analysis.maxccm]) - if (vmin == None and vmax == None): - vmax = np.nanmax(np.abs(difference_spectrum)) - vmin = -vmax - contlevels = np.linspace(vmin*0.5, vmax*0.5, 20) - - plt.contourf(xas_analysis.ccm_bins, xas_analysis.time_bins, - difference_spectrum, contlevels, cmap='RdBu', extend="max") - plt.colorbar() - plt.xlabel('Energy (keV)') - plt.ylabel('Time (ps)')
- - -# def plot_2d_difference_spectrum_real_axes(self,xas_analysis): -# laser_on_spectrum=xas_analysis.summed_laser_on/xas_analysis.summed_norm_on -# laser_off_spectrum=np.divide(np.nansum(xas_analysis.summed_laser_off,axis=0),np.nansum(xas_analysis.summed_norm_off,axis=0)) -# difference_spectrum=laser_on_spectrum-laser_off_spectrum -# vmin, vmax = np.percentile(difference_spectrum, [0,99]) -# plt.figure(dpi=100) -# plt.imshow(difference_spectrum.T, cmap='RdBu', vmin=self.vmin, vmax=self.vmax, origin='lower',aspect='auto',extent=[xas_analysis.mintime,xas_analysis.maxtime,xas_analysis.minccm,xas_analysis.maxccm]) -# plt.contourf( -# plt.colorbar() -# plt.xlabel('Time (ps)') -# plt.ylabel('Energy (keV)') -# setattr(xas_analysis,'difference_spectrum',difference_spectrum) - -
-[docs] - def plot_1d_difference_time(self, xas_analysis): - laser_on = xas_analysis.summed_laser_on/xas_analysis.summed_norm_on - laser_off = xas_analysis.summed_laser_off/xas_analysis.summed_norm_off - - difference = laser_on - laser_off - setattr(xas_analysis, 'difference_trace', difference) - plt.figure(dpi=100) - plt.plot(xas_analysis.time_bins, difference) - plt.xlabel('Time (ps)') - plt.ylabel(r'$\Delta XAS$')
- - -
-[docs] - def plot_1d_difference_spectrum(self, xas_analysis): - laser_on = xas_analysis.summed_laser_on/xas_analysis.summed_norm_on - laser_off = xas_analysis.summed_laser_off/xas_analysis.summed_norm_off - - difference = laser_on - laser_off - setattr(xas_analysis, 'difference_trace', difference) - plt.figure(dpi=100) - plt.plot(xas_analysis.ccm_bins, difference) - plt.xlabel('Energy (keV)') - plt.ylabel(r'$\Delta XAS$')
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/index.html b/docs/_build/html/_modules/index.html deleted file mode 100644 index 599540a..0000000 --- a/docs/_build/html/_modules/index.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - Overview: module code — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
- - -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_sources/XSpect.rst.txt b/docs/_build/html/_sources/XSpect.rst.txt deleted file mode 100644 index 40cadd1..0000000 --- a/docs/_build/html/_sources/XSpect.rst.txt +++ /dev/null @@ -1,53 +0,0 @@ -XSpect package -============== - -Submodules ----------- - -XSpect.XSpect\_Analysis module ------------------------------- - -.. automodule:: XSpect.XSpect_Analysis - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_Controller module --------------------------------- - -.. automodule:: XSpect.XSpect_Controller - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_Diagnostics module ---------------------------------- - -.. automodule:: XSpect.XSpect_Diagnostics - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_PostProcessing module ------------------------------------- - -.. automodule:: XSpect.XSpect_PostProcessing - :members: - :undoc-members: - :show-inheritance: - -XSpect.XSpect\_Visualization module ------------------------------------ - -.. automodule:: XSpect.XSpect_Visualization - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: XSpect - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/_build/html/_sources/index.rst.txt b/docs/_build/html/_sources/index.rst.txt deleted file mode 100644 index cc54905..0000000 --- a/docs/_build/html/_sources/index.rst.txt +++ /dev/null @@ -1,19 +0,0 @@ -.. XSpecT documentation master file, created by - sphinx-quickstart on Fri Sep 6 14:34:23 2024. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -XSpecT documentation -==================== - -Add your content using ``reStructuredText`` syntax. See the -`reStructuredText `_ -documentation for details. - - -.. toctree:: - :maxdepth: 2 - - modules - - diff --git a/docs/_build/html/_sources/modules.rst.txt b/docs/_build/html/_sources/modules.rst.txt deleted file mode 100644 index a61a475..0000000 --- a/docs/_build/html/_sources/modules.rst.txt +++ /dev/null @@ -1,7 +0,0 @@ -XSpecT -====== - -.. toctree:: - :maxdepth: 4 - - XSpect diff --git a/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js b/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js deleted file mode 100644 index 8141580..0000000 --- a/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js +++ /dev/null @@ -1,123 +0,0 @@ -/* Compatability shim for jQuery and underscores.js. - * - * Copyright Sphinx contributors - * Released under the two clause BSD licence - */ - -/** - * small helper function to urldecode strings - * - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL - */ -jQuery.urldecode = function(x) { - if (!x) { - return x - } - return decodeURIComponent(x.replace(/\+/g, ' ')); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} diff --git a/docs/_build/html/_static/basic.css b/docs/_build/html/_static/basic.css deleted file mode 100644 index f316efc..0000000 --- a/docs/_build/html/_static/basic.css +++ /dev/null @@ -1,925 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 360px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a:visited { - color: #551A8B; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.sig dd { - margin-top: 0px; - margin-bottom: 0px; -} - -.sig dl { - margin-top: 0px; - margin-bottom: 0px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -.translated { - background-color: rgba(207, 255, 207, 0.2) -} - -.untranslated { - background-color: rgba(255, 207, 207, 0.2) -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file 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 c718cee..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-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}} \ 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 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 19a446a..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>a:hover{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 img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.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-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}}.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%}}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 4d67807..0000000 --- a/docs/_build/html/_static/doctools.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Base JavaScript utilities for all Sphinx HTML documentation. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"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 e21c068..0000000 --- a/docs/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '0.1', - 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/file.png b/docs/_build/html/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/docs/_build/html/_static/file.png and /dev/null differ diff --git a/docs/_build/html/_static/jquery.js b/docs/_build/html/_static/jquery.js deleted file mode 100644 index c4c6022..0000000 --- a/docs/_build/html/_static/jquery.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/_build/html/_static/js/html5shiv.min.js b/docs/_build/html/_static/js/html5shiv.min.js deleted file mode 100644 index cd1c674..0000000 --- a/docs/_build/html/_static/js/html5shiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/_build/html/_static/js/theme.js b/docs/_build/html/_static/js/theme.js deleted file mode 100644 index 1fddb6e..0000000 --- a/docs/_build/html/_static/js/theme.js +++ /dev/null @@ -1 +0,0 @@ -!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - diff --git a/docs/_build/html/_static/minus.png b/docs/_build/html/_static/minus.png deleted file mode 100644 index d96755f..0000000 Binary files a/docs/_build/html/_static/minus.png and /dev/null differ diff --git a/docs/_build/html/_static/plus.png b/docs/_build/html/_static/plus.png deleted file mode 100644 index 7107cec..0000000 Binary files a/docs/_build/html/_static/plus.png and /dev/null differ diff --git a/docs/_build/html/_static/pygments.css b/docs/_build/html/_static/pygments.css deleted file mode 100644 index 84ab303..0000000 --- a/docs/_build/html/_static/pygments.css +++ /dev/null @@ -1,75 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #9C6500 } /* Comment.Preproc */ -.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ -.highlight .gr { color: #E40000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #008400 } /* Generic.Inserted */ -.highlight .go { color: #717171 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0044DD } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #687822 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #767600 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mb { color: #666666 } /* Literal.Number.Bin */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #0000FF } /* Name.Function.Magic */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .vm { color: #19177C } /* Name.Variable.Magic */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_build/html/_static/searchtools.js b/docs/_build/html/_static/searchtools.js deleted file mode 100644 index b08d58c..0000000 --- a/docs/_build/html/_static/searchtools.js +++ /dev/null @@ -1,620 +0,0 @@ -/* - * searchtools.js - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for the full-text search. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -/** - * Simple result scoring code. - */ -if (typeof Scorer === "undefined") { - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [docname, title, anchor, descr, score, filename] - // and returns the new score. - /* - score: result => { - const [docname, title, anchor, descr, score, filename] = result - return score - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5, // used to be unimportantResults - }, - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2, - }; -} - -const _removeChildren = (element) => { - while (element && element.lastChild) element.removeChild(element.lastChild); -}; - -/** - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - */ -const _escapeRegExp = (string) => - string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string - -const _displayItem = (item, searchTerms, highlightTerms) => { - const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; - const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; - const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; - const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - const contentRoot = document.documentElement.dataset.content_root; - - const [docName, title, anchor, descr, score, _filename] = item; - - let listItem = document.createElement("li"); - let requestUrl; - let linkUrl; - if (docBuilder === "dirhtml") { - // dirhtml builder - let dirname = docName + "/"; - if (dirname.match(/\/index\/$/)) - dirname = dirname.substring(0, dirname.length - 6); - else if (dirname === "index/") dirname = ""; - requestUrl = contentRoot + dirname; - linkUrl = requestUrl; - } else { - // normal html builders - requestUrl = contentRoot + docName + docFileSuffix; - linkUrl = docName + docLinkSuffix; - } - let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; - linkEl.innerHTML = title; - if (descr) { - listItem.appendChild(document.createElement("span")).innerHTML = - " (" + descr + ")"; - // highlight search terms in the description - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - } - else if (showSearchSummary) - fetch(requestUrl) - .then((responseData) => responseData.text()) - .then((data) => { - if (data) - listItem.appendChild( - Search.makeSearchSummary(data, searchTerms, anchor) - ); - // highlight search terms in the summary - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - }); - Search.output.appendChild(listItem); -}; -const _finishSearch = (resultCount) => { - Search.stopPulse(); - Search.title.innerText = _("Search Results"); - if (!resultCount) - Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." - ); - else - Search.status.innerText = _( - "Search finished, found ${resultCount} page(s) matching the search query." - ).replace('${resultCount}', resultCount); -}; -const _displayNextItem = ( - results, - resultCount, - searchTerms, - highlightTerms, -) => { - // results left, load the summary and display it - // this is intended to be dynamic (don't sub resultsCount) - if (results.length) { - _displayItem(results.pop(), searchTerms, highlightTerms); - setTimeout( - () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), - 5 - ); - } - // search finished, update title and status message - else _finishSearch(resultCount); -}; -// Helper function used by query() to order search results. -// Each input is an array of [docname, title, anchor, descr, score, filename]. -// Order the results by score (in opposite order of appearance, since the -// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. -const _orderResultsByScoreThenName = (a, b) => { - const leftScore = a[4]; - const rightScore = b[4]; - if (leftScore === rightScore) { - // same score: sort alphabetically - const leftTitle = a[1].toLowerCase(); - const rightTitle = b[1].toLowerCase(); - if (leftTitle === rightTitle) return 0; - return leftTitle > rightTitle ? -1 : 1; // inverted is intentional - } - return leftScore > rightScore ? 1 : -1; -}; - -/** - * Default splitQuery function. Can be overridden in ``sphinx.search`` with a - * custom function per language. - * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. - */ -if (typeof splitQuery === "undefined") { - var splitQuery = (query) => query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter(term => term) // remove remaining empty strings -} - -/** - * Search Module - */ -const Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - - htmlToText: (htmlString, anchor) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - for (const removalQuery of [".headerlink", "script", "style"]) { - htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); - } - if (anchor) { - const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); - if (anchorContent) return anchorContent.textContent; - - console.warn( - `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` - ); - } - - // if anchor not specified or not found, fall back to main content - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent) return docContent.textContent; - - console.warn( - "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." - ); - return ""; - }, - - init: () => { - const query = new URLSearchParams(window.location.search).get("q"); - document - .querySelectorAll('input[name="q"]') - .forEach((el) => (el.value = query)); - if (query) Search.performSearch(query); - }, - - loadIndex: (url) => - (document.body.appendChild(document.createElement("script")).src = url), - - setIndex: (index) => { - Search._index = index; - if (Search._queued_query !== null) { - const query = Search._queued_query; - Search._queued_query = null; - Search.query(query); - } - }, - - hasIndex: () => Search._index !== null, - - deferQuery: (query) => (Search._queued_query = query), - - stopPulse: () => (Search._pulse_status = -1), - - startPulse: () => { - if (Search._pulse_status >= 0) return; - - const pulse = () => { - Search._pulse_status = (Search._pulse_status + 1) % 4; - Search.dots.innerText = ".".repeat(Search._pulse_status); - if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: (query) => { - // create the required interface elements - const searchText = document.createElement("h2"); - searchText.textContent = _("Searching"); - const searchSummary = document.createElement("p"); - searchSummary.classList.add("search-summary"); - searchSummary.innerText = ""; - const searchList = document.createElement("ul"); - searchList.classList.add("search"); - - const out = document.getElementById("search-results"); - Search.title = out.appendChild(searchText); - Search.dots = Search.title.appendChild(document.createElement("span")); - Search.status = out.appendChild(searchSummary); - Search.output = out.appendChild(searchList); - - const searchProgress = document.getElementById("search-progress"); - // Some themes don't use the search progress node - if (searchProgress) { - searchProgress.innerText = _("Preparing search..."); - } - Search.startPulse(); - - // index already loaded, the browser was quick! - if (Search.hasIndex()) Search.query(query); - else Search.deferQuery(query); - }, - - _parseQuery: (query) => { - // stem the search terms and add them to the correct list - const stemmer = new Stemmer(); - const searchTerms = new Set(); - const excludedTerms = new Set(); - const highlightTerms = new Set(); - const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); - splitQuery(query.trim()).forEach((queryTerm) => { - const queryTermLower = queryTerm.toLowerCase(); - - // maybe skip this "word" - // stopwords array is from language_data.js - if ( - stopwords.indexOf(queryTermLower) !== -1 || - queryTerm.match(/^\d+$/) - ) - return; - - // stem the word - let word = stemmer.stemWord(queryTermLower); - // select the correct list - if (word[0] === "-") excludedTerms.add(word.substr(1)); - else { - searchTerms.add(word); - highlightTerms.add(queryTermLower); - } - }); - - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) - } - - // console.debug("SEARCH: searching for:"); - // console.info("required: ", [...searchTerms]); - // console.info("excluded: ", [...excludedTerms]); - - return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; - }, - - /** - * execute search (requires search index to be loaded) - */ - _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - - // Collect multiple result groups to be sorted separately and then ordered. - // Each is an array of [docname, title, anchor, descr, score, filename]. - const normalResults = []; - const nonMainIndexResults = []; - - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase().trim(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - const score = Math.round(Scorer.title * queryLower.length / title.length); - const boost = titles[file] === title ? 1 : 0; // add a boost for document titles - normalResults.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score + boost, - filenames[file], - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { - for (const [file, id, isMain] of foundEntries) { - const score = Math.round(100 * queryLower.length / entry.length); - const result = [ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - ]; - if (isMain) { - normalResults.push(result); - } else { - nonMainIndexResults.push(result); - } - } - } - } - - // lookup as object - objectTerms.forEach((term) => - normalResults.push(...Search.performObjectSearch(term, objectTerms)) - ); - - // lookup as search terms in fulltext - normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) { - normalResults.forEach((item) => (item[4] = Scorer.score(item))); - nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); - } - - // Sort each group of results by score and then alphabetically by name. - normalResults.sort(_orderResultsByScoreThenName); - nonMainIndexResults.sort(_orderResultsByScoreThenName); - - // Combine the result groups in (reverse) order. - // Non-main index entries are typically arbitrary cross-references, - // so display them after other results. - let results = [...nonMainIndexResults, ...normalResults]; - - // remove duplicate search results - // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept - let seen = new Set(); - results = results.reverse().reduce((acc, result) => { - let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); - if (!seen.has(resultStr)) { - acc.push(result); - seen.add(resultStr); - } - return acc; - }, []); - - return results.reverse(); - }, - - query: (query) => { - const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); - const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); - - // for debugging - //Search.lastresults = results.slice(); // a copy - // console.info("search results:", Search.lastresults); - - // print the results - _displayNextItem(results, results.length, searchTerms, highlightTerms); - }, - - /** - * search for object names - */ - performObjectSearch: (object, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const objects = Search._index.objects; - const objNames = Search._index.objnames; - const titles = Search._index.titles; - - const results = []; - - const objectSearchCallback = (prefix, match) => { - const name = match[4] - const fullname = (prefix ? prefix + "." : "") + name; - const fullnameLower = fullname.toLowerCase(); - if (fullnameLower.indexOf(object) < 0) return; - - let score = 0; - const parts = fullnameLower.split("."); - - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullnameLower === object || parts.slice(-1)[0] === object) - score += Scorer.objNameMatch; - else if (parts.slice(-1)[0].indexOf(object) > -1) - score += Scorer.objPartialMatch; // matches in last name - - const objName = objNames[match[1]][2]; - const title = titles[match[0]]; - - // If more than one term searched for, we require other words to be - // found in the name/title/description - const otherTerms = new Set(objectTerms); - otherTerms.delete(object); - if (otherTerms.size > 0) { - const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); - if ( - [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) - ) - return; - } - - let anchor = match[3]; - if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; - - const descr = objName + _(", in ") + title; - - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) - score += Scorer.objPrio[match[2]]; - else score += Scorer.objPrioDefault; - - results.push([ - docNames[match[0]], - fullname, - "#" + anchor, - descr, - score, - filenames[match[0]], - ]); - }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) - ); - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch: (searchTerms, excludedTerms) => { - // prepare search - const terms = Search._index.terms; - const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - - const scoreMap = new Map(); - const fileMap = new Map(); - - // perform the search on the required terms - searchTerms.forEach((word) => { - const files = []; - const arr = [ - { files: terms[word], score: Scorer.term }, - { files: titleTerms[word], score: Scorer.title }, - ]; - // add support for partial matches - if (word.length > 2) { - const escapedWord = _escapeRegExp(word); - if (!terms.hasOwnProperty(word)) { - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - } - if (!titleTerms.hasOwnProperty(word)) { - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); - }); - } - } - - // no match but word was a required one - if (arr.every((record) => record.files === undefined)) return; - - // found search word in contents - arr.forEach((record) => { - if (record.files === undefined) return; - - let recordFiles = record.files; - if (recordFiles.length === undefined) recordFiles = [recordFiles]; - files.push(...recordFiles); - - // set score for the word in each file - recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, {}); - scoreMap.get(file)[word] = record.score; - }); - }); - - // create the mapping - files.forEach((file) => { - if (!fileMap.has(file)) fileMap.set(file, [word]); - else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); - }); - }); - - // now check if the files don't contain excluded terms - const results = []; - for (const [file, wordList] of fileMap) { - // check if all requirements are matched - - // as search terms with length < 3 are discarded - const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2 - ).length; - if ( - wordList.length !== searchTerms.size && - wordList.length !== filteredTermCount - ) - continue; - - // ensure that none of the excluded terms is in the search result - if ( - [...excludedTerms].some( - (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) - ) - ) - break; - - // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); - // add result to the result list - results.push([ - docNames[file], - titles[file], - "", - null, - score, - filenames[file], - ]); - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words. - */ - makeSearchSummary: (htmlText, keywords, anchor) => { - const text = Search.htmlToText(htmlText, anchor); - if (text === "") return null; - - const textLower = text.toLowerCase(); - const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) - .filter((i) => i > -1) - .slice(-1)[0]; - const startWithContext = Math.max(actualStartPosition - 120, 0); - - const top = startWithContext === 0 ? "" : "..."; - const tail = startWithContext + 240 < text.length ? "..." : ""; - - let summary = document.createElement("p"); - summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; - - return summary; - }, -}; - -_ready(Search.init); diff --git a/docs/_build/html/_static/sphinx_highlight.js b/docs/_build/html/_static/sphinx_highlight.js deleted file mode 100644 index 8a96c69..0000000 --- a/docs/_build/html/_static/sphinx_highlight.js +++ /dev/null @@ -1,154 +0,0 @@ -/* Highlighting utilities for Sphinx HTML documentation. */ -"use strict"; - -const SPHINX_HIGHLIGHT_ENABLED = true - -/** - * highlight a given string on a node by wrapping it in - * span elements with the given class name. - */ -const _highlight = (node, addItems, text, className) => { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; - const parent = node.parentNode; - const pos = val.toLowerCase().indexOf(text); - if ( - pos >= 0 && - !parent.classList.contains(className) && - !parent.classList.contains("nohighlight") - ) { - let span; - - const closestNode = parent.closest("body, svg, foreignObject"); - const isInSVG = closestNode && closestNode.matches("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.classList.add(className); - } - - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - const rest = document.createTextNode(val.substr(pos + text.length)); - parent.insertBefore( - span, - parent.insertBefore( - rest, - node.nextSibling - ) - ); - node.nodeValue = val.substr(0, pos); - /* There may be more occurrences of search term in this node. So call this - * function recursively on the remaining fragment. - */ - _highlight(rest, addItems, text, className); - - if (isInSVG) { - const rect = document.createElementNS( - "http://www.w3.org/2000/svg", - "rect" - ); - const bbox = parent.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute("class", className); - addItems.push({ parent: parent, target: rect }); - } - } - } else if (node.matches && !node.matches("button, select, textarea")) { - node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); - } -}; -const _highlightText = (thisNode, text, className) => { - let addItems = []; - _highlight(thisNode, addItems, text, className); - addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target) - ); -}; - -/** - * Small JavaScript module for the documentation. - */ -const SphinxHighlight = { - - /** - * highlight the search words provided in localstorage in the text - */ - highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight - - // get and clear terms from localstorage - const url = new URL(window.location); - const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms") - url.searchParams.delete("highlight"); - window.history.replaceState({}, "", url); - - // get individual terms from highlight string - const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); - if (terms.length === 0) return; // nothing to do - - // There should never be more than one element matching "div.body" - const divBody = document.querySelectorAll("div.body"); - const body = divBody.length ? divBody[0] : document.querySelector("body"); - window.setTimeout(() => { - terms.forEach((term) => _highlightText(body, term, "highlighted")); - }, 10); - - const searchBox = document.getElementById("searchbox"); - if (searchBox === null) return; - searchBox.appendChild( - document - .createRange() - .createContextualFragment( - '" - ) - ); - }, - - /** - * 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/genindex.html b/docs/_build/html/genindex.html deleted file mode 100644 index 7d49406..0000000 --- a/docs/_build/html/genindex.html +++ /dev/null @@ -1,577 +0,0 @@ - - - - - - Index — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Index

- -
- A - | B - | C - | D - | E - | F - | G - | H - | I - | K - | L - | M - | N - | P - | R - | S - | T - | U - | V - | X - -
-

A

- - - -
- -

B

- - - -
- -

C

- - - -
- -

D

- - -
- -

E

- - - -
- -

F

- - - -
- -

G

- - - -
- -

H

- - - -
- -

I

- - - -
- -

K

- - -
- -

L

- - - -
- -

M

- - -
- -

N

- - -
- -

P

- - - -
- -

R

- - - -
- -

S

- - - -
- -

T

- - - -
- -

U

- - - -
- -

V

- - -
- -

X

- - - -
    -
  • - XSpect - -
  • -
  • - XSpect.XSpect_Analysis - -
  • -
  • - XSpect.XSpect_Controller - -
  • -
  • - XSpect.XSpect_Diagnostics - -
  • -
  • - XSpect.XSpect_PostProcessing - -
  • -
  • - XSpect.XSpect_Visualization - -
  • -
- - - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html deleted file mode 100644 index c443571..0000000 --- a/docs/_build/html/index.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - XSpecT documentation — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

XSpecT documentation

-

Add your content using reStructuredText syntax. See the -reStructuredText -documentation for details.

-
- -
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/modules.html b/docs/_build/html/modules.html deleted file mode 100644 index 962f664..0000000 --- a/docs/_build/html/modules.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - XSpecT — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

XSpecT

-
- -
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/objects.inv b/docs/_build/html/objects.inv deleted file mode 100644 index 8250b94..0000000 Binary files a/docs/_build/html/objects.inv and /dev/null differ diff --git a/docs/_build/html/py-modindex.html b/docs/_build/html/py-modindex.html deleted file mode 100644 index 1f17a22..0000000 --- a/docs/_build/html/py-modindex.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - Python Module Index — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Python Module Index

- -
- x -
- - - - - - - - - - - - - - - - - - - - - - -
 
- x
- XSpect -
    - XSpect.XSpect_Analysis -
    - XSpect.XSpect_Controller -
    - XSpect.XSpect_Diagnostics -
    - XSpect.XSpect_PostProcessing -
    - XSpect.XSpect_Visualization -
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/search.html b/docs/_build/html/search.html deleted file mode 100644 index de9f01b..0000000 --- a/docs/_build/html/search.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - Search — XSpecT 0.1 documentation - - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - - - -
- -
- -
-
- -
-
-
-
- - - - - - - - - \ No newline at end of file diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js deleted file mode 100644 index 4e459bf..0000000 --- a/docs/_build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles": {"Module contents": [[0, "module-XSpect"]], "Parameters": [[0, "parameters"], [0, "id1"], [0, "id2"], [0, "id3"], [0, "id6"], [0, "id7"], [0, "id8"], [0, "id9"], [0, "id10"], [0, "id11"], [0, "id12"], [0, "id13"], [0, "id14"], [0, "id15"], [0, "id16"], [0, "id17"], [0, "id18"], [0, "id19"], [0, "id20"], [0, "id21"], [0, "id22"], [0, "id24"], [0, "id26"], [0, "id27"], [0, "id28"], [0, "id29"]], "Raises": [[0, "raises"]], "Returns": [[0, "returns"], [0, "id4"], [0, "id5"], [0, "id23"], [0, "id25"]], "Submodules": [[0, "submodules"]], "XSpecT": [[2, null]], "XSpecT documentation": [[1, null]], "XSpect package": [[0, null]], "XSpect.XSpect_Analysis module": [[0, "module-XSpect.XSpect_Analysis"]], "XSpect.XSpect_Controller module": [[0, "module-XSpect.XSpect_Controller"]], "XSpect.XSpect_Diagnostics module": [[0, "module-XSpect.XSpect_Diagnostics"]], "XSpect.XSpect_PostProcessing module": [[0, "module-XSpect.XSpect_PostProcessing"]], "XSpect.XSpect_Visualization module": [[0, "module-XSpect.XSpect_Visualization"]]}, "docnames": ["XSpect", "index", "modules"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1}, "filenames": ["XSpect.rst", "index.rst", "modules.rst"], "indexentries": {"add_detector() (xspect.xspect_analysis.spectroscopy_experiment method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_experiment.add_detector", false]], "add_filter() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.add_filter", false]], "adu_histogram() (xspect.xspect_diagnostics.diagnostics method)": [[0, "XSpect.XSpect_Diagnostics.diagnostics.adu_histogram", false]], "analysis_functions (class in xspect.xspect_postprocessing)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions", false]], "analyze_single_run() (in module xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.analyze_single_run", false]], "batchanalysis (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.BatchAnalysis", false]], "bin_uniques() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.bin_uniques", false]], "break_into_shot_ranges() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.break_into_shot_ranges", false]], "ccm_binning() (xspect.xspect_analysis.xasanalysis method)": [[0, "XSpect.XSpect_Analysis.XASAnalysis.ccm_binning", false]], "close_h5() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.close_h5", false]], "combine_spectra() (xspect.xspect_visualization.xasvisualization method)": [[0, "XSpect.XSpect_Visualization.XASVisualization.combine_spectra", false]], "combine_spectra() (xspect.xspect_visualization.xesvisualization method)": [[0, "XSpect.XSpect_Visualization.XESVisualization.combine_spectra", false]], "combine_static_spectra() (xspect.xspect_visualization.xesvisualization method)": [[0, "XSpect.XSpect_Visualization.XESVisualization.combine_static_spectra", false]], "construct_theta() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.construct_theta", false]], "diagnostics (class in xspect.xspect_diagnostics)": [[0, "XSpect.XSpect_Diagnostics.diagnostics", false]], "experiment (class in xspect.xspect_analysis)": [[0, "XSpect.XSpect_Analysis.experiment", false]], "expfunc() (xspect.xspect_postprocessing.analysis_functions method)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions.expfunc", false]], "expfunc_heaviside() (xspect.xspect_postprocessing.analysis_functions method)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions.expfunc_heaviside", false]], "filter_detector_adu() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.filter_detector_adu", false]], "filter_nan() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.filter_nan", false]], "filter_shots() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.filter_shots", false]], "gaussfunc() (xspect.xspect_postprocessing.analysis_functions method)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions.gaussfunc", false]], "gaussfunc_norm() (xspect.xspect_postprocessing.analysis_functions method)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions.gaussfunc_norm", false]], "get_experiment_directory() (xspect.xspect_analysis.experiment method)": [[0, "XSpect.XSpect_Analysis.experiment.get_experiment_directory", false]], "get_run_shot_properties() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.get_run_shot_properties", false]], "get_scan_val() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.get_scan_val", false]], "hit_find() (xspect.xspect_controller.xesbatchanalysisrotation method)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysisRotation.hit_find", false]], "hplot() (xspect.xspect_diagnostics.plotting method)": [[0, "XSpect.XSpect_Diagnostics.plotting.hplot", false]], "ipm_histogram() (xspect.xspect_diagnostics.diagnostics method)": [[0, "XSpect.XSpect_Diagnostics.diagnostics.ipm_histogram", false]], "irfconv() (xspect.xspect_postprocessing.analysis_functions method)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions.irfconv", false]], "irfconv_ana() (xspect.xspect_postprocessing.analysis_functions method)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions.irfconv_ana", false]], "kmatsolver() (xspect.xspect_postprocessing.analysis_functions method)": [[0, "XSpect.XSpect_PostProcessing.analysis_functions.kmatsolver", false]], "load_run_key_delayed() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.load_run_key_delayed", false]], "load_run_keys() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.load_run_keys", false]], "load_run_keys() (xspect.xspect_diagnostics.diagnostics method)": [[0, "XSpect.XSpect_Diagnostics.diagnostics.load_run_keys", false]], "load_sum_run_scattering() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.load_sum_run_scattering", false]], "make_ccm_axis() (xspect.xspect_analysis.xasanalysis method)": [[0, "XSpect.XSpect_Analysis.XASAnalysis.make_ccm_axis", false]], "make_energy_axis() (xspect.xspect_analysis.xesanalysis method)": [[0, "XSpect.XSpect_Analysis.XESAnalysis.make_energy_axis", false]], "module": [[0, "module-XSpect", false], [0, "module-XSpect.XSpect_Analysis", false], [0, "module-XSpect.XSpect_Controller", false], [0, "module-XSpect.XSpect_Diagnostics", false], [0, "module-XSpect.XSpect_PostProcessing", false], [0, "module-XSpect.XSpect_Visualization", false]], "normalize_xes() (xspect.xspect_analysis.xesanalysis method)": [[0, "XSpect.XSpect_Analysis.XESAnalysis.normalize_xes", false]], "parse_run_shots() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.parse_run_shots", false]], "parse_theta() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.parse_theta", false]], "patch_pixel() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.patch_pixel", false]], "patch_pixel_1d() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.patch_pixel_1d", false]], "patch_pixels() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.patch_pixels", false]], "patch_pixels_1d() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.patch_pixels_1d", false]], "plot_1d_difference_spectrum() (xspect.xspect_visualization.xasvisualization method)": [[0, "XSpect.XSpect_Visualization.XASVisualization.plot_1d_difference_spectrum", false]], "plot_1d_difference_time() (xspect.xspect_visualization.xasvisualization method)": [[0, "XSpect.XSpect_Visualization.XASVisualization.plot_1d_difference_time", false]], "plot_1d_xes() (xspect.xspect_visualization.xesvisualization method)": [[0, "XSpect.XSpect_Visualization.XESVisualization.plot_1d_XES", false]], "plot_2d_difference_spectrum() (xspect.xspect_visualization.spectroscopyvisualization method)": [[0, "XSpect.XSpect_Visualization.SpectroscopyVisualization.plot_2d_difference_spectrum", false]], "plot_2d_difference_spectrum() (xspect.xspect_visualization.xasvisualization method)": [[0, "XSpect.XSpect_Visualization.XASVisualization.plot_2d_difference_spectrum", false]], "plot_2d_difference_spectrum() (xspect.xspect_visualization.xesvisualization method)": [[0, "XSpect.XSpect_Visualization.XESVisualization.plot_2d_difference_spectrum", false]], "plot_2d_spectrum() (xspect.xspect_visualization.spectroscopyvisualization method)": [[0, "XSpect.XSpect_Visualization.SpectroscopyVisualization.plot_2d_spectrum", false]], "plot_xas() (xspect.xspect_visualization.xasvisualization method)": [[0, "XSpect.XSpect_Visualization.XASVisualization.plot_XAS", false]], "plotting (class in xspect.xspect_diagnostics)": [[0, "XSpect.XSpect_Diagnostics.plotting", false]], "plotting (class in xspect.xspect_postprocessing)": [[0, "XSpect.XSpect_PostProcessing.plotting", false]], "post_analysis (class in xspect.xspect_postprocessing)": [[0, "XSpect.XSpect_PostProcessing.post_analysis", false]], "primary_analysis() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.primary_analysis", false]], "primary_analysis() (xspect.xspect_controller.scananalysis_1d method)": [[0, "XSpect.XSpect_Controller.ScanAnalysis_1D.primary_analysis", false]], "primary_analysis() (xspect.xspect_controller.scananalysis_1d_xes method)": [[0, "XSpect.XSpect_Controller.ScanAnalysis_1D_XES.primary_analysis", false]], "primary_analysis() (xspect.xspect_controller.xasbatchanalysis method)": [[0, "XSpect.XSpect_Controller.XASBatchAnalysis.primary_analysis", false]], "primary_analysis() (xspect.xspect_controller.xasbatchanalysis_1d_ccm method)": [[0, "XSpect.XSpect_Controller.XASBatchAnalysis_1D_ccm.primary_analysis", false]], "primary_analysis() (xspect.xspect_controller.xasbatchanalysis_1d_time method)": [[0, "XSpect.XSpect_Controller.XASBatchAnalysis_1D_time.primary_analysis", false]], "primary_analysis() (xspect.xspect_controller.xesbatchanalysis method)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysis.primary_analysis", false]], "primary_analysis() (xspect.xspect_controller.xesbatchanalysisrotation method)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysisRotation.primary_analysis", false]], "primary_analysis_loop() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.primary_analysis_loop", false]], "primary_analysis_parallel_loop() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.primary_analysis_parallel_loop", false]], "primary_analysis_parallel_range() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.primary_analysis_parallel_range", false]], "primary_analysis_range() (xspect.xspect_controller.xesbatchanalysisrotation method)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysisRotation.primary_analysis_range", false]], "primary_analysis_static() (xspect.xspect_controller.xesbatchanalysisrotation method)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysisRotation.primary_analysis_static", false]], "primary_analysis_static_parallel_loop() (xspect.xspect_controller.xesbatchanalysisrotation method)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysisRotation.primary_analysis_static_parallel_loop", false]], "purge_all_keys() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.purge_all_keys", false]], "purge_keys() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.purge_keys", false]], "read_theta() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.read_theta", false]], "reduce_det_scanvar() (xspect.xspect_analysis.xesanalysis method)": [[0, "XSpect.XSpect_Analysis.XESAnalysis.reduce_det_scanvar", false]], "reduce_detector_ccm() (xspect.xspect_analysis.xasanalysis method)": [[0, "XSpect.XSpect_Analysis.XASAnalysis.reduce_detector_ccm", false]], "reduce_detector_ccm_temporal() (xspect.xspect_analysis.xasanalysis method)": [[0, "XSpect.XSpect_Analysis.XASAnalysis.reduce_detector_ccm_temporal", false]], "reduce_detector_shots() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.reduce_detector_shots", false]], "reduce_detector_spatial() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.reduce_detector_spatial", false]], "reduce_detector_spatial() (xspect.xspect_analysis.xesanalysis method)": [[0, "XSpect.XSpect_Analysis.XESAnalysis.reduce_detector_spatial", false]], "reduce_detector_temporal() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.reduce_detector_temporal", false]], "reduce_detector_temporal() (xspect.xspect_analysis.xasanalysis method)": [[0, "XSpect.XSpect_Analysis.XASAnalysis.reduce_detector_temporal", false]], "roiview() (xspect.xspect_diagnostics.plotting method)": [[0, "XSpect.XSpect_Diagnostics.plotting.roiview", false]], "run_parser() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.run_parser", false]], "scananalysis_1d (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.ScanAnalysis_1D", false]], "scananalysis_1d_xes (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.ScanAnalysis_1D_XES", false]], "separate_shots() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.separate_shots", false]], "set_key_aliases() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.set_key_aliases", false]], "spectroscopy_experiment (class in xspect.xspect_analysis)": [[0, "XSpect.XSpect_Analysis.spectroscopy_experiment", false]], "spectroscopy_run (class in xspect.xspect_analysis)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run", false]], "spectroscopyanalysis (class in xspect.xspect_analysis)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis", false]], "spectroscopyvisualization (class in xspect.xspect_visualization)": [[0, "XSpect.XSpect_Visualization.SpectroscopyVisualization", false]], "svdplot() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.svdplot", false]], "svdreconstruct() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.svdreconstruct", false]], "targetanalysis_run() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.targetanalysis_run", false]], "targetobjective() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.targetobjective", false]], "time_binning() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.time_binning", false]], "trim_ccm() (xspect.xspect_analysis.xasanalysis method)": [[0, "XSpect.XSpect_Analysis.XASAnalysis.trim_ccm", false]], "ttampl_histogram() (xspect.xspect_diagnostics.diagnostics method)": [[0, "XSpect.XSpect_Diagnostics.diagnostics.ttAMPL_histogram", false]], "union_shots() (xspect.xspect_analysis.spectroscopyanalysis method)": [[0, "XSpect.XSpect_Analysis.SpectroscopyAnalysis.union_shots", false]], "update_status() (xspect.xspect_analysis.spectroscopy_run method)": [[0, "XSpect.XSpect_Analysis.spectroscopy_run.update_status", false]], "update_status() (xspect.xspect_controller.batchanalysis method)": [[0, "XSpect.XSpect_Controller.BatchAnalysis.update_status", false]], "varproj() (xspect.xspect_postprocessing.post_analysis method)": [[0, "XSpect.XSpect_PostProcessing.post_analysis.varproj", false]], "xas_roi() (xspect.xspect_diagnostics.diagnostics method)": [[0, "XSpect.XSpect_Diagnostics.diagnostics.xas_ROI", false]], "xasanalysis (class in xspect.xspect_analysis)": [[0, "XSpect.XSpect_Analysis.XASAnalysis", false]], "xasbatchanalysis (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.XASBatchAnalysis", false]], "xasbatchanalysis_1d_ccm (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.XASBatchAnalysis_1D_ccm", false]], "xasbatchanalysis_1d_time (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.XASBatchAnalysis_1D_time", false]], "xasvisualization (class in xspect.xspect_visualization)": [[0, "XSpect.XSpect_Visualization.XASVisualization", false]], "xes_roi() (xspect.xspect_diagnostics.diagnostics method)": [[0, "XSpect.XSpect_Diagnostics.diagnostics.xes_ROI", false]], "xesanalysis (class in xspect.xspect_analysis)": [[0, "XSpect.XSpect_Analysis.XESAnalysis", false]], "xesbatchanalysis (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysis", false]], "xesbatchanalysisrotation (class in xspect.xspect_controller)": [[0, "XSpect.XSpect_Controller.XESBatchAnalysisRotation", false]], "xesvisualization (class in xspect.xspect_visualization)": [[0, "XSpect.XSpect_Visualization.XESVisualization", false]], "xspect": [[0, "module-XSpect", false]], "xspect.xspect_analysis": [[0, "module-XSpect.XSpect_Analysis", false]], "xspect.xspect_controller": [[0, "module-XSpect.XSpect_Controller", false]], "xspect.xspect_diagnostics": [[0, "module-XSpect.XSpect_Diagnostics", false]], "xspect.xspect_postprocessing": [[0, "module-XSpect.XSpect_PostProcessing", false]], "xspect.xspect_visualization": [[0, "module-XSpect.XSpect_Visualization", false]]}, "objects": {"": [[0, 0, 0, "-", "XSpect"]], "XSpect": [[0, 0, 0, "-", "XSpect_Analysis"], [0, 0, 0, "-", "XSpect_Controller"], [0, 0, 0, "-", "XSpect_Diagnostics"], [0, 0, 0, "-", "XSpect_PostProcessing"], [0, 0, 0, "-", "XSpect_Visualization"]], "XSpect.XSpect_Analysis": [[0, 1, 1, "", "SpectroscopyAnalysis"], [0, 1, 1, "", "XASAnalysis"], [0, 1, 1, "", "XESAnalysis"], [0, 1, 1, "", "experiment"], [0, 1, 1, "", "spectroscopy_experiment"], [0, 1, 1, "", "spectroscopy_run"]], "XSpect.XSpect_Analysis.SpectroscopyAnalysis": [[0, 2, 1, "", "bin_uniques"], [0, 2, 1, "", "filter_detector_adu"], [0, 2, 1, "", "filter_nan"], [0, 2, 1, "", "filter_shots"], [0, 2, 1, "", "patch_pixel"], [0, 2, 1, "", "patch_pixel_1d"], [0, 2, 1, "", "patch_pixels"], [0, 2, 1, "", "patch_pixels_1d"], [0, 2, 1, "", "purge_keys"], [0, 2, 1, "", "reduce_detector_shots"], [0, 2, 1, "", "reduce_detector_spatial"], [0, 2, 1, "", "reduce_detector_temporal"], [0, 2, 1, "", "separate_shots"], [0, 2, 1, "", "time_binning"], [0, 2, 1, "", "union_shots"]], "XSpect.XSpect_Analysis.XASAnalysis": [[0, 2, 1, "", "ccm_binning"], [0, 2, 1, "", "make_ccm_axis"], [0, 2, 1, "", "reduce_detector_ccm"], [0, 2, 1, "", "reduce_detector_ccm_temporal"], [0, 2, 1, "", "reduce_detector_temporal"], [0, 2, 1, "", "trim_ccm"]], "XSpect.XSpect_Analysis.XESAnalysis": [[0, 2, 1, "", "make_energy_axis"], [0, 2, 1, "", "normalize_xes"], [0, 2, 1, "", "reduce_det_scanvar"], [0, 2, 1, "", "reduce_detector_spatial"]], "XSpect.XSpect_Analysis.experiment": [[0, 2, 1, "", "get_experiment_directory"]], "XSpect.XSpect_Analysis.spectroscopy_experiment": [[0, 2, 1, "", "add_detector"]], "XSpect.XSpect_Analysis.spectroscopy_run": [[0, 2, 1, "", "close_h5"], [0, 2, 1, "", "get_run_shot_properties"], [0, 2, 1, "", "get_scan_val"], [0, 2, 1, "", "load_run_key_delayed"], [0, 2, 1, "", "load_run_keys"], [0, 2, 1, "", "load_sum_run_scattering"], [0, 2, 1, "", "purge_all_keys"], [0, 2, 1, "", "update_status"]], "XSpect.XSpect_Controller": [[0, 1, 1, "", "BatchAnalysis"], [0, 1, 1, "", "ScanAnalysis_1D"], [0, 1, 1, "", "ScanAnalysis_1D_XES"], [0, 1, 1, "", "XASBatchAnalysis"], [0, 1, 1, "", "XASBatchAnalysis_1D_ccm"], [0, 1, 1, "", "XASBatchAnalysis_1D_time"], [0, 1, 1, "", "XESBatchAnalysis"], [0, 1, 1, "", "XESBatchAnalysisRotation"], [0, 3, 1, "", "analyze_single_run"]], "XSpect.XSpect_Controller.BatchAnalysis": [[0, 2, 1, "", "add_filter"], [0, 2, 1, "", "break_into_shot_ranges"], [0, 2, 1, "", "parse_run_shots"], [0, 2, 1, "", "primary_analysis"], [0, 2, 1, "", "primary_analysis_loop"], [0, 2, 1, "", "primary_analysis_parallel_loop"], [0, 2, 1, "", "primary_analysis_parallel_range"], [0, 2, 1, "", "run_parser"], [0, 2, 1, "", "set_key_aliases"], [0, 2, 1, "", "update_status"]], "XSpect.XSpect_Controller.ScanAnalysis_1D": [[0, 2, 1, "", "primary_analysis"]], "XSpect.XSpect_Controller.ScanAnalysis_1D_XES": [[0, 2, 1, "", "primary_analysis"]], "XSpect.XSpect_Controller.XASBatchAnalysis": [[0, 2, 1, "", "primary_analysis"]], "XSpect.XSpect_Controller.XASBatchAnalysis_1D_ccm": [[0, 2, 1, "", "primary_analysis"]], "XSpect.XSpect_Controller.XASBatchAnalysis_1D_time": [[0, 2, 1, "", "primary_analysis"]], "XSpect.XSpect_Controller.XESBatchAnalysis": [[0, 2, 1, "", "primary_analysis"]], "XSpect.XSpect_Controller.XESBatchAnalysisRotation": [[0, 2, 1, "", "hit_find"], [0, 2, 1, "", "primary_analysis"], [0, 2, 1, "", "primary_analysis_range"], [0, 2, 1, "", "primary_analysis_static"], [0, 2, 1, "", "primary_analysis_static_parallel_loop"]], "XSpect.XSpect_Diagnostics": [[0, 1, 1, "", "diagnostics"], [0, 1, 1, "", "plotting"]], "XSpect.XSpect_Diagnostics.diagnostics": [[0, 2, 1, "", "adu_histogram"], [0, 2, 1, "", "ipm_histogram"], [0, 2, 1, "", "load_run_keys"], [0, 2, 1, "", "ttAMPL_histogram"], [0, 2, 1, "", "xas_ROI"], [0, 2, 1, "", "xes_ROI"]], "XSpect.XSpect_Diagnostics.plotting": [[0, 2, 1, "", "hplot"], [0, 2, 1, "", "roiview"]], "XSpect.XSpect_PostProcessing": [[0, 1, 1, "", "analysis_functions"], [0, 1, 1, "", "plotting"], [0, 1, 1, "", "post_analysis"]], "XSpect.XSpect_PostProcessing.analysis_functions": [[0, 2, 1, "", "expfunc"], [0, 2, 1, "", "expfunc_heaviside"], [0, 2, 1, "", "gaussfunc"], [0, 2, 1, "", "gaussfunc_norm"], [0, 2, 1, "", "irfconv"], [0, 2, 1, "", "irfconv_ana"], [0, 2, 1, "", "kmatsolver"]], "XSpect.XSpect_PostProcessing.post_analysis": [[0, 2, 1, "", "construct_theta"], [0, 2, 1, "", "parse_theta"], [0, 2, 1, "", "read_theta"], [0, 2, 1, "", "svdplot"], [0, 2, 1, "", "svdreconstruct"], [0, 2, 1, "", "targetanalysis_run"], [0, 2, 1, "", "targetobjective"], [0, 2, 1, "", "varproj"]], "XSpect.XSpect_Visualization": [[0, 1, 1, "", "SpectroscopyVisualization"], [0, 1, 1, "", "XASVisualization"], [0, 1, 1, "", "XESVisualization"]], "XSpect.XSpect_Visualization.SpectroscopyVisualization": [[0, 2, 1, "", "plot_2d_difference_spectrum"], [0, 2, 1, "", "plot_2d_spectrum"]], "XSpect.XSpect_Visualization.XASVisualization": [[0, 2, 1, "", "combine_spectra"], [0, 2, 1, "", "plot_1d_difference_spectrum"], [0, 2, 1, "", "plot_1d_difference_time"], [0, 2, 1, "", "plot_2d_difference_spectrum"], [0, 2, 1, "", "plot_XAS"]], "XSpect.XSpect_Visualization.XESVisualization": [[0, 2, 1, "", "combine_spectra"], [0, 2, 1, "", "combine_static_spectra"], [0, 2, 1, "", "plot_1d_XES"], [0, 2, 1, "", "plot_2d_difference_spectrum"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"], "3": ["py", "function", "Python function"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method", "3": "py:function"}, "terms": {"0": 0, "05": 0, "0e4": 0, "1": 0, "10000": 0, "120": 0, "1d": 0, "1e4": 0, "20": 0, "3": 0, "300": 0, "4": 0, "550": 0, "6": 0, "80": 0, "895": 0, "A": 0, "If": 0, "It": 0, "NOT": 0, "Not": 0, "The": 0, "These": 0, "To": 0, "_scanvar_reduc": 0, "accord": 0, "across": 0, "add": 1, "add_detector": [0, 2], "add_filt": [0, 2], "adu": 0, "adu_cutoff": 0, "adu_histogram": [0, 2], "adu_threshold": 0, "affect": 0, "after": 0, "again": 0, "align": 0, "all": 0, "along": 0, "amp": 0, "ampl": 0, "amplitud": 0, "an": 0, "analyi": 0, "analysi": 0, "analysis_funct": [0, 2], "analyze_single_run": [0, 2], "angl": 0, "append": 0, "appli": 0, "ar": 0, "arbitrari": 0, "arg": 0, "around": 0, "arrai": 0, "array_lik": 0, "attempt": 0, "attibut": 0, "attribut": 0, "averag": 0, "avoid": 0, "awai": 0, "axi": 0, "base": 0, "batchanalysi": [0, 2], "being": 0, "below": 0, "bin": 0, "bin_uniqu": [0, 2], "bodi": 0, "bool": 0, "bounds_dict": 0, "bragg_angle_calcul": 0, "break_into_shot_rang": [0, 2], "calcul": 0, "can": 0, "cannot": 0, "ccm": 0, "ccm_bin": [0, 2], "ccm_bin_key_indic": 0, "ccm_bins_kei": 0, "ccm_kei": 0, "center": 0, "center_in": 0, "chosen": 0, "class": 0, "close": 0, "close_h5": [0, 2], "combin": 0, "combine_spectra": [0, 2], "combine_static_spectra": [0, 2], "compar": 0, "configur": 0, "consid": 0, "construct_theta": [0, 2], "content": [1, 2], "control": 0, "convers": 0, "core": 0, "correct": 0, "correspond": 0, "creat": 0, "crystal": 0, "cutoff": 0, "d": 0, "daq": 0, "data": 0, "dataset": 0, "datetim": 0, "default": 0, "deg": 0, "degre": 0, "delai": 0, "detail": 1, "detector": 0, "detector_dimens": 0, "detector_kei": 0, "detector_nam": 0, "determin": 0, "diagnost": [0, 2], "did": 0, "differ": 0, "dimens": 0, "directori": 0, "distanc": 0, "due": 0, "e": 0, "each": 0, "enc": 0, "encod": 0, "end_col": 0, "end_index": 0, "energi": 0, "energy_axis_length": 0, "energy_dispersive_axi": 0, "epic": 0, "epix": 0, "epix_2": 0, "epix_roi_1": 0, "exampl": 0, "except": 0, "exp": 0, "experi": [0, 2], "experiment_id": 0, "expfunc": [0, 2], "expfunc_heavisid": [0, 2], "extend": 0, "f": 0, "fals": 0, "fast": 0, "fast_delay_kei": 0, "fewer": 0, "file": 0, "filter": 0, "filter_detector_adu": [0, 2], "filter_kei": 0, "filter_nan": [0, 2], "filter_shot": [0, 2], "find": 0, "first": 0, "fit": 0, "flag": 0, "float": 0, "follow": 0, "form": 0, "format": 0, "found": 0, "friendli": 0, "friendly_nam": 0, "from": 0, "function": 0, "g": 0, "gaussfunc": [0, 2], "gaussfunc_norm": [0, 2], "gener": 0, "get_experiment_directori": [0, 2], "get_run_shot_properti": [0, 2], "get_scan_v": [0, 2], "given": 0, "go": 0, "ha": 0, "had": 0, "handl": 0, "hdf5": 0, "high": 0, "hit_find": [0, 2], "horiz_limit": 0, "hplot": [0, 2], "hutch": 0, "i": 0, "id": 0, "imag": 0, "immedi": 0, "implement": 0, "includ": 0, "incorpor": 0, "increment": 0, "index": 0, "indic": 0, "inf": 0, "instanc": 0, "instead": 0, "int": 0, "integ": 0, "integr": 0, "interest": 0, "interpol": 0, "invers": 0, "ipm": 0, "ipm4": 0, "ipm_histogram": [0, 2], "irf_opt": 0, "irfconv": [0, 2], "irfconv_ana": [0, 2], "issu": 0, "its": 0, "k": 0, "k_in": 0, "ka_limit": 0, "kb_limit": 0, "kbeta": 0, "keep": 0, "kei": 0, "keys_to_keep": 0, "kmatrix": 0, "kmatsolv": [0, 2], "kwarg": 0, "larger": 0, "lasdelai": 0, "laser": 0, "lcl": 0, "lcls_run": 0, "leg_titl": 0, "like": 0, "list": 0, "load": 0, "load_run_kei": [0, 2], "load_run_key_delai": [0, 2], "load_sum_run_scatt": [0, 2], "log": 0, "low": 0, "lxt_kei": 0, "lxt_ttc": 0, "make_ccm_axi": [0, 2], "make_energy_axi": [0, 2], "mani": 0, "mask": 0, "me": 0, "mean": 0, "memori": 0, "messag": 0, "meta": 0, "method": 0, "minimum": 0, "misnom": 0, "mm": 0, "mm_per_pixel": 0, "mode": 0, "modul": 2, "multipl": 0, "name": 0, "nan": 0, "ncomp": 0, "ndarrai": 0, "need": 0, "new": 0, "nois": 0, "none": 0, "norm_kei": 0, "norm_laser_kei": 0, "normal": 0, "normalize_x": [0, 2], "not_ccm": 0, "np": 0, "nshot": 0, "number": 0, "numer": 0, "numpi": 0, "object": 0, "offset": 0, "one": 0, "ones": 0, "oom": 0, "option": 0, "order": 0, "origin": 0, "other": 0, "out": 0, "output": 0, "over": 0, "packag": [1, 2], "parse_run_shot": [0, 2], "parse_theta": [0, 2], "patch": 0, "patch_pixel": [0, 2], "patch_pixel_1d": [0, 2], "patch_pixels_1d": [0, 2], "patch_rang": 0, "path": 0, "per": 0, "perform": 0, "photon": 0, "pixel": 0, "pixel_arrai": 0, "pixel_rang": 0, "pl": 0, "plot": [0, 2], "plot_1d_difference_spectrum": [0, 2], "plot_1d_difference_tim": [0, 2], "plot_1d_x": [0, 2], "plot_2d_difference_spectrum": [0, 2], "plot_2d_spectrum": [0, 2], "plot_xa": [0, 2], "plt_titl": 0, "plt_type": 0, "point": 0, "poly_rang": 0, "polynomi": 0, "post_analysi": [0, 2], "primary_analysi": [0, 2], "primary_analysis_loop": [0, 2], "primary_analysis_parallel_loop": [0, 2], "primary_analysis_parallel_rang": [0, 2], "primary_analysis_rang": [0, 2], "primary_analysis_stat": [0, 2], "primary_analysis_static_parallel_loop": [0, 2], "print": 0, "printopt": 0, "process": 0, "properti": 0, "purg": 0, "purge_all_kei": [0, 2], "purge_kei": [0, 2], "r": 0, "radii": 0, "rai": 0, "rang": 0, "read_theta": [0, 2], "recent": 0, "reduc": 0, "reduce_det_scanvar": [0, 2], "reduce_detector_ccm": [0, 2], "reduce_detector_ccm_tempor": [0, 2], "reduce_detector_shot": [0, 2], "reduce_detector_spati": [0, 2], "reduce_detector_tempor": [0, 2], "reduct": 0, "reduction_funct": 0, "region": 0, "remov": 0, "repres": 0, "requir": 0, "restructuredtext": 1, "result": 0, "retain": 0, "retriev": 0, "ring": 0, "roi": 0, "roi_0_area": 0, "roiopt": 0, "roiview": [0, 2], "roughli": 0, "row": 0, "run": 0, "run_arrai": 0, "run_pars": [0, 2], "runengin": 0, "s3df": 0, "save": 0, "scan": 0, "scananalysis_1d": [0, 2], "scananalysis_1d_x": [0, 2], "scanvar_bins_kei": 0, "scanvar_kei": 0, "scatter": 0, "see": 1, "separ": 0, "separate_shot": [0, 2], "set": 0, "set_key_alias": [0, 2], "setroi": 0, "shot": 0, "shot_mask_kei": 0, "shot_rang": 0, "shot_typ": 0, "should": 0, "sigma": 0, "sigma_in": 0, "signal": 0, "simultan": 0, "singl": 0, "so": 0, "some": 0, "sourc": 0, "space": 0, "spatial": 0, "spec_experi": 0, "special": 0, "specif": 0, "specifi": 0, "spectra": 0, "spectral": 0, "spectroscopi": 0, "spectroscopy_experi": [0, 2], "spectroscopy_run": [0, 2], "spectroscopyanalysi": [0, 2], "spectroscopyvisu": [0, 2], "start_col": 0, "start_index": 0, "statu": 0, "step": 0, "store": 0, "str": 0, "stretch": 0, "string": 0, "submodul": 2, "subsequ": 0, "sum": 0, "support": 0, "svdplot": [0, 2], "svdreconstruct": [0, 2], "switch": 0, "syntax": 1, "tag": 0, "target_kei": 0, "targetanalysis_run": [0, 2], "targetobject": [0, 2], "tempor": 0, "than": 0, "theta": 0, "theta_pars": 0, "thi": 0, "those": 0, "thre": 0, "threshold": 0, "through": 0, "time": 0, "time_bin": [0, 2], "time_tool_ampl": 0, "time_tool_correct": 0, "timing_bin_indic": 0, "timing_bin_key_indic": 0, "todo": 0, "tool": 0, "total": 0, "transpos": 0, "trim": 0, "trim_ccm": [0, 2], "true": 0, "try": 0, "tt": 0, "tt_correction_kei": 0, "ttampl_histogram": [0, 2], "ttcorr": 0, "under": 0, "union_shot": [0, 2], "uniqu": 0, "updat": 0, "update_statu": [0, 2], "us": [0, 1], "valu": 0, "variabl": 0, "variou": 0, "varproj": [0, 2], "verbos": 0, "vert": 0, "vert_limit": 0, "vh": 0, "visit": 0, "vmax": 0, "vmin": 0, "want": 0, "water": 0, "we": 0, "well": 0, "were": 0, "whether": 0, "which": 0, "within": 0, "without": 0, "work": 0, "would": 0, "x": 0, "x0": 0, "x0_in": 0, "xa": 0, "xas_analysi": 0, "xas_kei": 0, "xas_laser_kei": 0, "xas_roi": [0, 2], "xasanalysi": [0, 2], "xasbatchanalysi": [0, 2], "xasbatchanalysis_1d_ccm": [0, 2], "xasbatchanalysis_1d_tim": [0, 2], "xasvisu": [0, 2], "xe": 0, "xes_analysi": 0, "xes_kei": 0, "xes_laser_kei": 0, "xes_lin": 0, "xes_roi": [0, 2], "xesanalysi": [0, 2], "xesbatchanalysi": [0, 2], "xesbatchanalysisrot": [0, 2], "xesvisu": [0, 2], "xlabel": 0, "xrai": 0, "xspect_analysi": 2, "xspect_control": 2, "xspect_diagnost": 2, "xspect_postprocess": 2, "xspect_visu": 2, "xval": 0, "y": 0, "year": 0, "you": 0, "your": 1, "yscale": 0, "yval": 0}, "titles": ["XSpect package", "XSpecT documentation", "XSpecT"], "titleterms": {"content": 0, "document": 1, "modul": 0, "packag": 0, "paramet": 0, "rais": 0, "return": 0, "submodul": 0, "xspect": [0, 1, 2], "xspect_analysi": 0, "xspect_control": 0, "xspect_diagnost": 0, "xspect_postprocess": 0, "xspect_visu": 0}}) \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 3ee04a7..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,36 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - -import os -import sys - -sys.path.insert(0, os.path.abspath("..")) - -project = 'XSpecT' -copyright = '2024, Leland B. Gee' -author = 'Leland B. Gee' -release = '0.1' - -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -# extensions = ["sphinx.ext.todo", "sphinx.ext.viewcode", "sphinx.ext.autodoc"] -extensions = [ - 'sphinx.ext.duration', - 'sphinx.ext.doctest', - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx', -] - -templates_path = ['_templates'] -# exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'sphinx_rtd_theme' -# html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.md similarity index 58% rename from docs/index.rst rename to docs/index.md index 7c2e628..94705ee 100644 --- a/docs/index.rst +++ b/docs/index.md @@ -1,26 +1,18 @@ -Overview -==================== +# Overview -*XSpecT* is an analysis tool for ultrafast x-ray experiments. -It is written in python_ following the principles of object-oriented programming. +XSpecT is an analysis software package designed for ultrafast x-ray free electron laser experiments. It is written in [python](https://www.python.org/) following the principles of object-oriented programming. -Installation --------------------- +## Installation -You can download the *XSpecT* source code from the github repo_. +The source code is available for download from the [XSpecT github repo](https://github.com/lg345/XSpecT). -Documentation --------------------- +## Getting Started -Detailed documentation of the code can be found here_. +Check out our quick start examples for [XAS](Getting_started_XAS.html) and [XES](Getting_started_XES.html), as well as the example jupyter noteboks found [here](https://github.com/lg345/XSpecT/tree/master/examples). -Addtionally, example jupyter-notebooks can be found in the examples_ folder. +## License - -License --------------------- - -Copyright 2024 XSpecT Team +Copyright 2025 XSpecT Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -28,15 +20,3 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Links --------------------- -.. toctree:: - :maxdepth: 2 - - index - modules - -.. _python: https://www.python.org/ -.. _repo: https://github.com/lg345/XSpecT -.. _here: modules.html -.. _examples: https://github.com/lg345/XSpecT/tree/master/examples diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 32bb245..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/media/2Dplot.png b/docs/media/2Dplot.png new file mode 100644 index 0000000..93e4b2c Binary files /dev/null and b/docs/media/2Dplot.png differ diff --git a/docs/media/77615fc07aea654fbf2201cddc214b1008b2608d.png b/docs/media/77615fc07aea654fbf2201cddc214b1008b2608d.png new file mode 100644 index 0000000..e0c43f1 Binary files /dev/null and b/docs/media/77615fc07aea654fbf2201cddc214b1008b2608d.png differ diff --git a/docs/media/diagram.png b/docs/media/diagram.png new file mode 100644 index 0000000..334b728 Binary files /dev/null and b/docs/media/diagram.png differ diff --git a/docs/media/f22364f0824ce6092523ca08e6a277dd8abc3f46.png b/docs/media/f22364f0824ce6092523ca08e6a277dd8abc3f46.png new file mode 100644 index 0000000..8201898 Binary files /dev/null and b/docs/media/f22364f0824ce6092523ca08e6a277dd8abc3f46.png differ diff --git a/docs/media/image10.png b/docs/media/image10.png new file mode 100644 index 0000000..6c9bd77 Binary files /dev/null and b/docs/media/image10.png differ diff --git a/docs/media/image11.png b/docs/media/image11.png new file mode 100644 index 0000000..1d5cc44 Binary files /dev/null and b/docs/media/image11.png differ diff --git a/docs/media/image12.png b/docs/media/image12.png new file mode 100644 index 0000000..ed4167d Binary files /dev/null and b/docs/media/image12.png differ diff --git a/docs/media/image13.png b/docs/media/image13.png new file mode 100644 index 0000000..6d58c32 Binary files /dev/null and b/docs/media/image13.png differ diff --git a/docs/media/image14.png b/docs/media/image14.png new file mode 100644 index 0000000..09da3d4 Binary files /dev/null and b/docs/media/image14.png differ diff --git a/docs/media/image8.png b/docs/media/image8.png new file mode 100644 index 0000000..868a7ba Binary files /dev/null and b/docs/media/image8.png differ diff --git a/docs/media/image9.png b/docs/media/image9.png new file mode 100644 index 0000000..5af060b Binary files /dev/null and b/docs/media/image9.png differ diff --git a/docs/media/laseroff.png b/docs/media/laseroff.png new file mode 100644 index 0000000..4c857e0 Binary files /dev/null and b/docs/media/laseroff.png differ diff --git a/docs/modules.rst b/docs/modules.rst deleted file mode 100644 index a61a475..0000000 --- a/docs/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -XSpecT -====== - -.. toctree:: - :maxdepth: 4 - - XSpect diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index d146bf6..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -sphinx==7.1.2 -sphinx-rtd-theme==1.3.0rc1 -h5py==3.12.1 -numpy==2.1.3 -matplotlib==3.9.2 -scipy==1.14.1 -tqdm==4.67.0 -psutil==6.1.0 diff --git a/examples/Getting_started_XAS.ipynb b/examples/Getting_started_XAS.ipynb new file mode 100644 index 0000000..ad727f6 --- /dev/null +++ b/examples/Getting_started_XAS.ipynb @@ -0,0 +1,820 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b5968cd6-53b0-4c31-adae-071186dee9b3", + "metadata": {}, + "source": [ + "## Importing Dependencies \n", + "XSpecT relies on a number of common python packages including: \n", + "- h5py for reading HDF5 files\n", + "- NumPy and scipy for data analysis\n", + "- Matplotlib for visualization\n", + "- Other system related packages\n", + "\n", + "Depending on your system you may need to install the necessary dependencies. S3DF users should have the necessary packages by default." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "becb847b-01e0-4237-9938-50d60725ab90", + "metadata": {}, + "outputs": [], + "source": [ + "import h5py\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from scipy.ndimage import rotate\n", + "from scipy.interpolate import interp1d\n", + "from scipy.optimize import curve_fit,minimize\n", + "import multiprocessing\n", + "import os\n", + "from functools import partial\n", + "import time\n", + "import sys\n", + "import argparse\n", + "from datetime import datetime\n", + "import tempfile" + ] + }, + { + "cell_type": "markdown", + "id": "cc3caffd-b735-4cea-88a6-e632ec9e291e", + "metadata": {}, + "source": [ + "## Importing XSPecT Modules\n", + "XSpecT has several main modules for function to control various aspects of the analysis, visualization, diagnostics and overall processing." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f75e3561-3066-4c76-a1e1-661f4fc26be9", + "metadata": {}, + "outputs": [], + "source": [ + "sys.path.insert(0, './XSpecT/')\n", + "import XSpect.XSpect_Analysis\n", + "import XSpect.XSpect_Controller\n", + "import XSpect.XSpect_Visualization\n", + "import XSpect.XSpect_PostProcessing\n", + "import XSpect.XSpect_Diagnostics" + ] + }, + { + "cell_type": "markdown", + "id": "0e7c34da-a425-48d6-8cdc-abf8fab6b29c", + "metadata": {}, + "source": [ + "## XAS Analysis Example" + ] + }, + { + "cell_type": "markdown", + "id": "dd54b2d2-3e3e-427d-8b42-da5691254487", + "metadata": {}, + "source": [ + "### Setting up experiment parameters\n", + "Intializing the `spectroscopy_experiment` class and setting the relevant experiment information`lslc_run`, `hutch`, and `experiment_id` parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "28565581-bab0-4e42-b38a-fd03831da3f6", + "metadata": {}, + "outputs": [], + "source": [ + "xas_experiment = XSpect.XSpect_Analysis.spectroscopy_experiment(lcls_run=22, hutch='xcs', experiment_id='xcsl1030422')" + ] + }, + { + "cell_type": "markdown", + "id": "99d32fe4-1a80-4f10-836f-91ed194b7f22", + "metadata": {}, + "source": [ + "These values will be used to obtain the directory for the data which is stored in `experiment_directory`:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8f9ec965-6c38-4395-bc30-34a2274d5779", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/sdf/data/lcls/ds/xcs/xcsl1030422/hdf5/smalldata'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "xas_experiment.experiment_directory" + ] + }, + { + "cell_type": "markdown", + "id": "e4fa4739-0205-4df8-9503-58c8d5b64bbf", + "metadata": {}, + "source": [ + "### XASBatchAnalysis Class" + ] + }, + { + "cell_type": "markdown", + "id": "4775d4c2-9550-44e0-81b0-a0453498c8df", + "metadata": {}, + "source": [ + "Instantiating the `XASBatchAnalysis` class which allows you to set attributes relevant to the analysis such as the HDF5 group keys for the various datasets, filter thresholds, and timing/energy parameters. The class also contain an analysis pipeline method, which controls the sequence of analysis operations. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "cc4dee4a-de30-47f1-b571-944fdf4d7a58", + "metadata": {}, + "outputs": [], + "source": [ + "xas=XSpect.XSpect_Controller.XASBatchAnalysis()" + ] + }, + { + "cell_type": "markdown", + "id": "4c154137-4ba6-43b1-ba72-2c3ff9f05791", + "metadata": {}, + "source": [ + "#### Setting keys and aliases\n", + "The keys, which specify the data to read from the HDF5 file, are defined as a list of strings. For pump-probe XAS measurements this typically includes: the monochromator energy and set values (epics/ccm_E, epicsUser/ccm_E_setpoint), TT correction values and amplitude (tt/ttCorr, tt/AMPL), timing stage values (epics/lxt_ttc), emission CCD ROI sum values (epix_2/ROI_0_sum) and normalization channel (ipm4/sum).\n", + "\n", + "Their \"friendly\" names serve as an easier to remember alias for the keys and are also defined as a list of strings with the same ordering as the keys. These lists are passed to `set_key_aliases` which creates the key aliases. " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "33913eb7-8767-4ab0-8283-82d9162d7b43", + "metadata": {}, + "outputs": [], + "source": [ + "keys=['epics/ccm_E', 'epicsUser/ccm_E_setpoint', 'tt/ttCorr', 'epics/lxt_ttc', 'enc/lasDelay', 'ipm4/sum', 'tt/AMPL', 'epix_2/ROI_0_sum'] \n", + "names=['ccm', 'ccm_E_setpoint', 'time_tool_correction', 'lxt_ttc', 'encoder', 'ipm', 'time_tool_ampl', 'epix']\n", + "xas.set_key_aliases(keys,names)" + ] + }, + { + "cell_type": "markdown", + "id": "80ff9eb2-e850-4e18-8d09-688041acc675", + "metadata": {}, + "source": [ + "#### Adding filters\n", + "Filters are set using `add_filter` which takes requires the parameters 'shot_type' (e.g. xray, simultaneous), 'filter_key' (i.e. which dataset to apply the filter to), and the filter threshold." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f7b733a3-e608-41d0-9ad4-2603271f3c14", + "metadata": {}, + "outputs": [], + "source": [ + "xas.add_filter('xray','ipm',500.0)\n", + "xas.add_filter('simultaneous','ipm',500.0)\n", + "xas.add_filter('simultaneous','time_tool_ampl',0.01)" + ] + }, + { + "cell_type": "markdown", + "id": "049b8b1a-2373-4962-914d-566fb1f07af5", + "metadata": {}, + "source": [ + "#### Setting runs\n", + "Multiple runs (files) can be analyzed and combined into a single data set using the `run_parser` method. Specify the runs as a list of strings or as a single string with space separated run numbers. Ranges can be specified using numbers separated by a '-'." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "a5f4f76d-1ac3-47f8-bdb5-ec281812ed84", + "metadata": {}, + "outputs": [], + "source": [ + "xas.run_parser(['240-243 245-254'])" + ] + }, + { + "cell_type": "markdown", + "id": "fa86b95e-3296-482c-b242-3d49036d0663", + "metadata": {}, + "source": [ + "#### Setting timing parameters\n", + "Delay timing range and number of points is set in picoseconds." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "981b833f-8c64-4e32-a333-33e713845714", + "metadata": {}, + "outputs": [], + "source": [ + "xas.mintime = -0.5\n", + "xas.maxtime = 2.0\n", + "xas.numpoints = 25" + ] + }, + { + "cell_type": "markdown", + "id": "74d879d5-238b-4f5b-a6d7-6bf9a5f6e95d", + "metadata": {}, + "source": [ + "#### Normalization option\n", + "Normalization is set by default (`False`) to use an IPM sum dataset. Alternatively, the scattering liquid ring signal can be used:" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "827022f8-8c59-4b32-94e5-c910dd56a779", + "metadata": {}, + "outputs": [], + "source": [ + "xas.scattering=True" + ] + }, + { + "cell_type": "markdown", + "id": "01792c9e-9e8b-4c10-8eba-75e0087ed68d", + "metadata": {}, + "source": [ + "### Running Analysis Loop\n", + "With the necessary parameters set the analysis procedure can be initiatilized. Here you pass the experiment attributes from `xas_experiment`. For details of the step by step analysis processes set \"verbose=True\" (False is the default)." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "cdb6cef1-7cea-4ca1-a746-fa7d865a20b9", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2645\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1904\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 100\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12182) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12182) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12182) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12182) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12182) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.03 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 3339\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2353\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 61\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (11614) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (11614) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (11614) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (11614) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (11614) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2441\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1751\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 71\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12068) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12068) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12068) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12068) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12068) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 3401\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2445\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 223\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (11888) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (11888) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (11888) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (11888) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (11888) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2942\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2091\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 113\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12000) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12000) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12000) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12000) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12000) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 3309\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2341\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 143\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (11758) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (11758) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (11758) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (11758) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (11758) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 3054\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2235\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 113\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (11818) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (11818) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (11818) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (11818) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (11818) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.03 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2713\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1916\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 121\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (11981) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (11981) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (11981) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (11981) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (11981) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.03 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2453\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1743\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 63\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12114) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12114) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12114) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12114) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12114) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2238\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1607\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 114\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12233) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12233) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12233) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12233) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12233) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2415\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1723\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 73\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12144) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12144) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12144) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12144) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12144) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2507\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1781\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 62\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12037) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12037) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12037) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12037) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12037) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2845\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2087\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 169\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (11906) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (11906) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (11906) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (11906) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (11906) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n", + "Obtained shot properties\n", + "HDF5 import of keys completed. Time: 0.02 seconds\n", + "Mask: xray has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 2363\n", + "Mask: simultaneous has been filtered on ipm by minimum threshold: 500.000\n", + "Shots removed: 1675\n", + "Mask: simultaneous has been filtered on time_tool_ampl by minimum threshold: 0.010\n", + "Shots removed: 189\n", + "Shots combined for detector epix on filters: simultaneous and laser into epix_simultaneous_laser\n", + "Shots (12044) separated for detector epix on filters: xray and laser into epix_xray_laser\n", + "Shots combined for detector ipm on filters: simultaneous and laser into ipm_simultaneous_laser\n", + "Shots (12044) separated for detector ipm on filters: xray and laser into ipm_xray_laser\n", + "Shots combined for detector ccm on filters: simultaneous and laser into ccm_simultaneous_laser\n", + "Shots (12044) separated for detector ccm on filters: xray and laser into ccm_xray_laser\n", + "Generated timing bins from -0.500000 to 2.000000 in 25 steps.\n", + "Generated ccm bins from 7.105000 to 7.156500 in 54 steps.\n", + "Shots combined for detector timing_bin_indices on filters: simultaneous and laser into timing_bin_indices_simultaneous_laser\n", + "Shots (12044) separated for detector timing_bin_indices on filters: xray and laser into timing_bin_indices_xray_laser\n", + "Shots combined for detector ccm_bin_indices on filters: simultaneous and laser into ccm_bin_indices_simultaneous_laser\n", + "Shots (12044) separated for detector ccm_bin_indices on filters: xray and laser into ccm_bin_indices_xray_laser\n", + "Detector epix_simultaneous_laser binned in time into key: epix_simultaneous_laser_time_energy_binned\n", + "Detector epix_xray_not_laser binned in time into key: epix_xray_not_laser_time_energy_binned\n", + "Detector ipm_simultaneous_laser binned in time into key: ipm_simultaneous_laser_time_energy_binned\n", + "Detector ipm_xray_not_laser binned in time into key: ipm_xray_not_laser_time_energy_binned\n" + ] + } + ], + "source": [ + "xas.primary_analysis_loop(xas_experiment, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "30da52d6-4605-4765-b826-26b9916ef4c9", + "metadata": {}, + "source": [ + "#### Exploring Analyzed Runs\n", + "The data for each run is stored in `analyzed_runs` list. " + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "42ee90af-1700-4be9-b9e5-195c04bb27ed", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + " ]" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "xas.analyzed_runs" + ] + }, + { + "cell_type": "markdown", + "id": "76765c47-9357-4286-9be5-698ad687dad6", + "metadata": {}, + "source": [ + "We can check the data shape for the laser-off shots first analyzed run, which has the dimensions of 25 time bins by 54 energy bins. " + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "df9d6907-0aad-43fb-af64-c5509f26fa33", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data shape: (25, 54)\n" + ] + } + ], + "source": [ + "print(\"Data shape:\", xas.analyzed_runs[0].epix_xray_not_laser_time_energy_binned.shape)" + ] + }, + { + "cell_type": "markdown", + "id": "3c60f893-3a34-4ae6-88da-e409395f7748", + "metadata": {}, + "source": [ + "Since the laser the laser is off we can sum all the across all time bins for the epix and normalization channel." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "d364c2f2-cd24-4f12-a280-89c8c2869d57", + "metadata": {}, + "outputs": [], + "source": [ + "y = np.average(xas.analyzed_runs[0].epix_xray_not_laser_time_energy_binned, axis = 0)\n", + "norm = np.average(xas.analyzed_runs[0].ipm_xray_not_laser_time_energy_binned, axis =0)" + ] + }, + { + "cell_type": "markdown", + "id": "e36dc40d-4cbd-4db3-acf8-b33a2511a8f2", + "metadata": {}, + "source": [ + "#### Plotting Laser-off Spectrum\n", + "Then the laser off spectrum can be plotted versus the monochromator energies." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "f98a66ac-dec3-4040-96c8-be7bc3ee24f3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(xas.analyzed_runs[0].ccm_energies, y/norm, label=\"Laser-off\")\n", + "plt.xlabel(\"Energy (eV)\")\n", + "plt.ylabel(\"Normalized XAS\")\n", + "plt.legend()" + ] + }, + { + "cell_type": "markdown", + "id": "d25950a2-21fb-4e5d-848c-aa7fe0e1b438", + "metadata": {}, + "source": [ + "#### Plotting 2D Spectra\n", + "The 2D time versus energy can be summed and plotted using the XSpecT visualation module. First, from visualization the `XASVisualization` object is instantiated. Then, use the `combine_spectra` method passing the `xas` data object and the necessary data keys." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "2fc2dfc6-7f63-4a3c-923d-e0fa8420e1f3", + "metadata": {}, + "outputs": [], + "source": [ + "v=XSpect.XSpect_Visualization.XASVisualization()\n", + "v.combine_spectra(xas_analysis=xas,\n", + " xas_laser_key='epix_simultaneous_laser_time_energy_binned',\n", + " xas_key='epix_xray_not_laser_time_energy_binned',\n", + " norm_laser_key='ipm_simultaneous_laser_time_energy_binned',\n", + " norm_key='ipm_xray_not_laser_time_energy_binned')" + ] + }, + { + "cell_type": "markdown", + "id": "b05a08a1-842b-435a-b9e4-1187c1731913", + "metadata": {}, + "source": [ + "Finally, the 2D spectrum can be plotted, setting vmin and vmax colorbar parameters as needed." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "76ee472d-81fc-422c-9dc6-5addfb9b6f11", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqIAAAGcCAYAAAACmcKzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9eZwcdZ3//6qzeyaTEJJAIByBEDAokSByi1xGCciRYEQE5Qjr74vICiuwaDSgiIIXm1VwBYFkCQQWCMSDY+WQVTGIIkiCF2FhI4gJCRCSme7qOn5/VH2qP/Wpz6fqU0d3zyT1fDz6MTPVdU1P9fSrXu9L8TzPQ0VFRUVFRUVFRUWXUXt9AhUVFRUVFRUVFVsnlRCtqKioqKioqKjoCZUQraioqKioqKio6AmVEK2oqKioqKioqOgJlRCtqKioqKioqKjoCZUQraioqKioqKio6AmVEK2oqKioqKioqOgJlRCtqKioqKioqKjoCZUQraioqKioqKio6AkjVog++uijOOecczBt2jSMGjUKO+20E0466ST87ne/k9p+7dq1OOusszBhwgT09/fjkEMOwSOPPNLhs66oqKioqKioqCCMWCH6/e9/Hy+99BI++9nP4v7778fChQuxdu1aHHzwwXj00UcTt202mzjmmGPwyCOPYOHChVi+fDkmTpyIY489Fo8//niXfoOKioqKioqKiq0bZaTOml+7di223377yLJNmzZh6tSp2GefffDwww8Lt73++utx/vnn44knnsAhhxwCALBtG/vuuy8GBgbw5JNPdvTcKyoqKioqKioqAL3XJ5AXVoQCwMDAAN75zndizZo1idvee++9eMc73hGKUADQdR1nnHEGvvCFL+CVV17BTjvtxN128+bNwv2OGjVK8uwrKioqKiq2TKrPyYosjFghyuOtt97C008/jaOPPjpxvZUrV+Lwww+PLX/3u98NAFi1ahVXiLqui4GBgcTjV1RUVFRUbM1ss802wufWrFmDSZMmQVWHT2bgqlWrMGXKFPT19fX6VLZKtighev7552Pz5s2YP39+4nrr16/HuHHjYsvJsvXr13O3e/XVVxP3m/Tmq6ioqKio2NrZZZddsGbNGuy88869PhUAwBNPPIHDDjsM6sQZcF77fa9PZ6tkixGiX/rSl3Dbbbfhu9/9Lvbff//U9RVFyfzc6NGjAQDP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jyVLlmDNmjU499xzsXr1aixcuDDUKmnevHkwTRPr168P7ps7dy72228/zJw5EytWrMBjjz2GL37xi/jrX/+Ka665JrSv6667Dlu2bMGJJ56IVatW4f7778dJJ52Ejz76CD/60Y+C5S677DJ8/etfx4oVK/Cb3/wG999/P+bPn48f//jHmDlzJg4++OBg2e9///vYY489MHPmTNx666349a9/jXPPPRcPPPAALr/8crS18dMceAzaqnn6Tezq6sJee+2FlStXYvXq1bjtttuCN3HevHlYvnw5Xn/9dey5554AgOOPPx5HH300DjjggKBYaeHChdA0LfSmbAu0OixvMReiZk53yYOBkKMnWwCUhrSuJw+rWgOKhVTHVyoaKJo6iqaR/T0n88JjxKjXgoeapJTgjJLm9XlV0oryRGFrkVxIAm/SUn/nica5ollhXVG2ip5teA9mebI+nS8KB1xXNNhmTAU9C88pDRxSoB6qNyHljEq5ogluqLe5/ELyxAn11g+7omDyRO2aheLQduhFE2a5CLNcCoXlCVqxDNeqevfFhOeJCCXCubplK+yqhcqmXvRtbU2hk+s4qYdVhNbPa4Qsw7333ouLLroIl156aTAdcuXKlZHpkLZtw7btUFukUqmExx9/HBdeeCHOO+889PT0YOrUqXjkkUdCU5UALz/08ccfx8UXX4wvfelLAIAjjjgCa9euDfUhPeSQQ/C///f/xqpVq7BhwwaUy2Xsu+++uP766zF//vzQNnfaaSf89re/xYUXXojvf//72Lp1KyZOnIhbb71VOLlSxKAVooDcm8h7AydPnow777wT1113HXp7e7HLLrvg2GOPxSWXXIIJEyY09ZiN4Mt0G7IbMxB3AWx2D1HRMfAEQCvC8rx0AoCfKxrXwimr+LRpt8+MOh+kjRNv3GdwrH5IvmjqIUevjaqejyOojCYXgJiJSrQIJWJBmE/qNNjGxa4GoVzS8ocHmydav1/3Q9FOJDzfaBsnWTc0S9g9a54oIU6MAogN0dP5okB9cEDNcQF/Bj0Q/6NbJFJZUeoYhUxiVETcuSYTkg8da0JInnU8rYoFu+qP5y17rigpWqLhVc4DVFieDInw/0/C8yhHJy3RIrS6pScQzJVNvd4x9rVgYtUApqOjA4sWLcKiRYtil1u2bBl3quWuu+4amYQk4qijjsLatWtjl/nc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fz93vdGjR+Ovf/0rOjo6uPtQoXmFQqFQbO+IelK7rgvHcTB69Oh+PiLFQKblQnSfffbBkiVLpJbdY489AHiV7TzXc+PGjQDqzmgaxo4di6OOOgrPPPOMcBld1zFhgnJbFAqFQqEQMWzYsFYfgmIQ0XIhOmrUKJx99tmp1pk8eTJWrlwJy7JCeaIvv/wyAMTmecbhui50fYBllysUCoVCoVBsowxK1TVjxgx0d3fjnnvuCd2/fPlyjB49Gocffnjqbb755pt46qmncMQRR+R1mAqFQqFQKBSKGAalEJ0+fTpOOOEEzJ8/H0uWLMGaNWtw7rnnYvXq1Vi4cGGotdK8efNgmibWr18f3Hf88cfjhz/8IVatWoUnnngCixYtwlFHHQVN0/CjH/2oFU8pM2eddZZwSpSmabGpBn//+99x/vnn45hjjsEOO+wATdOwbNky7rIPPfQQvvKVr2Dy5MkoFAqJebjbGv3xOnd1deGqq67CtGnTsNtuu6GjowOTJ0/GNddcg0qlEt3wNkZ/ncsXXXQRDjzwQOy0004ol8sYN24czj333NB3xLZMf73ONL29vZgwYQI0TcN1112X47MZmPTXazxt2jTu9ukaCIVioNPy0HxW7r33Xlx00UW49NJLsXHjRkycOBErV67E6aefHlrOtm3Yth1qFzF58mTceeeduO6669Db24tddtkFxx57LC655JJBlwN6ySWX4Otf/3rk/s997nMolUo49NBDheu+9tpruP322zF16lScfPLJWLlypXDZ++67D8888wwOPPBAlEolvPDCC7kc/2ChP17nt99+Gz/72c/w5S9/Gd/97nfR0dGBJ598EpdffjkeffRRPProo9v0D4D+Opc3bdqEM844A5MmTcLQoUPxpz/9CVdeeSUeeOABvPLKK9v8dLX+ep3ZfYoKWLZF+vM1HjduHG6//fbQfTvssEOm41YoWoKr2OZYu3atC8C9+OKLY5ezbTv4//PPP+8CcJcuXZq47De/+U1XnTr5v87d3d1ud3d35P5rr73WBeA++eSTDR/zYKMZ5zKPhx9+2AXg3nLLLVkPdVDTzNf52WefdYvFonv33Xe7ANxrr702j0MedDTjNT7mmGPc/fbbL8/DVCj6nUHriCrE3HLLLdA0DXPnzo1dLk1hliriipL36yxq/3XYYYcBAN555510B7gN0IxzmcfO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DjvMveuuu9yHH37YnTZtmmuapvv6668Hy73yyivu8OHD3cmTJ7u/+MUv3F//+tfu9773PVfXdffyyy8PlluzZo0LwN19993d0047zX3ggQfchx56yN2wYYN74403ugDcL3zhC+5DDz3k3n777e6ECRPcPffc091zzz2DbSxatMjVNM3929/+FjrWxYsXuwDcV155Jfb5H3vsse5hhx0Wuf+rX/2qO2TIkODvO++80y2VSu78+fOD10X2edq27e65557u1KlTQ/uwLMsdNWqUe8QRR0T2/+yzz7oA3AceeCD2+BUKhWKwo4SoQrEdQ4Qo72YYRmhZAO6uu+7qdnV1Bfe9//77rq7r7k9+8pPgvhNPPNEdM2aMu3nz5tD63/rWt9xyuexu3LjRdd26ED366KNDy9m27e62227u4YcfHrp//fr1bqFQCAnRrq4ud+jQoe63v/3t0LL77ruv29nZmfj829vb3a9//euR+2khevXVV7uGYbjXXHNNaBnZ5+m6rnvZZZe5ANw//OEPwX0PPvigC8BdsmRJZP/VatXVNM3993//98TnoFAoFIMZFZpXKBT4xS9+geeffz50e/bZZyPLdXZ2YujQocHfu+66K3bZZResX78eAFCpVPD4449jxowZaG9vh2VZwe3kk09GpVLBM888E9rmF77whdDff/3rX/H+++/ji1/8Yuj+PfbYA0ceeWTovqFDh2LOnDlYtmxZEC5/4okn8Kc//Qnf+ta3Yp/zpk2b0NPTg1122YX7uOu6+NrXvobLLrsMK1aswIUXXhg8lvZ5zpkzB7qu49Zbbw3uW7p0KYYMGcJNCygUCthhhx3wj3/8I/Y5KBQKxWBHCVGFQoFJkybhkEMOCd0OPvjgyHJssQ0AlEol9Pb2AgA2bNgAy7Jwww03oFAohG4nn3wyAOCjjz4KrT9q1KjQ3xs2bADgiVwW3n3nnXcetmzZgttvvx0A8POf/xxjxozB5z//+djnTI65XC5zH69Wq7jzzjux3377Yfr06ZFjTPM899xzTxx33HFYsWIF+vr68NFHH+Ghhx7CzJkzQ8KeplwuB8eoUCgU2yqqal6hUOTGjjvuCMMw8OUvf1lYKPTxj3889DdbrU7E7j//+c/Iuu+//37kvr322gvTp0/H4sWLMX36dDzwwAO44oorYBhG7LGS/WzcuJH7eKlUwpo1a3DiiSfi+OOPx+rVq7Hjjjtmfp7z5s3Do48+ivvvvx/vvvsuqtUq5s2bJzy+f/3rXxg5cmTsc1AoFIrBjhKiCoUiN9rb29HZ2YkXX3wRBxxwAIrFYupt7LPPPthtt91w11134bvf/W5w/9tvv42nn34ao0ePjqzz7W9/G5/+9Kfx1a9+FYZh4JxzzkncT7FYxLhx4/D6668LlznwwAPxm9/8BscffzymTZuGRx99FLvsskum53nKKadgxIgRuPXWW/Hee+9hwoQJOOqoo7jLvvvuu6hUKttFH1eFQrF9o4SoQqHAunXrYFlW5P7x48dj5513TrWtRYsW4aijjsKnPvUpzJ8/H2PHjsWWLVvw2muv4cEHH8QTTzwRu76u67jiiivwta99Daeddhrmzp2LTZs24YorrsCoUaOg69GMohNOOAH77rsv1qxZg9mzZwvzPlmmTZuGRx55JHaZSZMm4cknn8Txxx+Po48+Go899hjGjBmT+nmWSiV86Utfwg033ADXdXH11VcL90nySzs7O6Weh0KhUAxWVI6oQqHAnDlz8IlPfCJyu//++1Nva99998Uf/vAH7L///rj44ovx6U9/GvPmzcOvfvUrHHfccVLbOPfcc3HTTTfhf/7nfzBjxgxcccUVWLBgAQ488EDssMMO3HVIcVNSkRLNl770Jbz33nt4/vnnY5cbN24cnnzySWiahk996lN44403Mj3PefPmwXVdGIaBr3zlK8L9rVq1CpMnT8bkyZOln4tCoVAMRjTXdd1WH4RCoVAksWnTJkyYMAGnnHIKd+LQIYccAk3TEkUlywEHHIAjjzwS/+f//J+8DrUhurq6MHr0aFx//fVSKQYKhUIxmFGheYVCMeB4//33cdVVV6GzsxMjRozA+vXrcf3112PLli349re/HSzX1dWFdevW4aGHHsILL7yA++67L/W+Fi5ciBkzZuCiiy7CmDFj8nwambj++uuxxx57YM6cOa0+FIVCoWg6SogqFIoBR6lUwltvvYVvfOMb2LhxI9rb23HEEUfg//7f/4v99tsvWO4Pf/hDIFYvu+wynHLKKan3ddJJJ+Haa6/Fm2++OSCE6LBhw7Bs2TKYpvp6VigU2z4qNK9QKBQKhUKhaAmqWEmhUCgUCoVC0RKUEFUoFAqFQqFQtAQlRBUKhUKhUCgULUEJUYVCoVAoFApFS1BCVKFQKBQKhULREpQQVSgUCoVCoVC0BCVEFQqFQqFQKBQtQQlRhUKhUCgUCkVL+P8giclscLfZ1wAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "v.plot_2d_difference_spectrum(xas, vmin=-0.3, vmax=0.3)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/media/77615fc07aea654fbf2201cddc214b1008b2608d.png b/media/77615fc07aea654fbf2201cddc214b1008b2608d.png new file mode 100644 index 0000000..e0c43f1 Binary files /dev/null and b/media/77615fc07aea654fbf2201cddc214b1008b2608d.png differ diff --git a/media/diagram.png b/media/diagram.png new file mode 100644 index 0000000..334b728 Binary files /dev/null and b/media/diagram.png differ diff --git a/media/f22364f0824ce6092523ca08e6a277dd8abc3f46.png b/media/f22364f0824ce6092523ca08e6a277dd8abc3f46.png new file mode 100644 index 0000000..8201898 Binary files /dev/null and b/media/f22364f0824ce6092523ca08e6a277dd8abc3f46.png differ diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..3b9b641 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,60 @@ +site_name: XSpecT +repo_url: https://github.com/lg345/XSpecT +site_author: JTB +copyright: © 2025 LCLS + +nav: + - Home: index.md + - Overview: Overview.md + - Examples: + - XAS: Getting_started_XAS.md + - XES: Getting_started_XES.md + - Post_Processing: Post_Processing.md + - Source: + - XSpect: + - XSpect_Analysis.md + - XSpect_Controller.md + - XSpect_Diagnostics.md + - XSpect_PostProcessing.md + - XSpect_Visualization.md + +plugins: +- search +- mkdocstrings: + handlers: + python: + options: + docstring_style: numpy +- offline + +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - admonition + - pymdownx.arithmatex: + generic: true + - footnotes + - pymdownx.details + - pymdownx.superfences + - pymdownx.mark + - attr_list + +theme: + name: material + logo: media/logo_2.png + favicon: media/logo.png + palette: + - scheme: default + primary: teal + accent: deep purple + toggle: + icon: material/brightness-6 + name: Switch to dark mode + - scheme: slate + primary: teal + accent: amber + toggle: + icon: material/brightness-5 + name: Switch to light mode