{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Basic usage\n\nBasic usage of building a model and fitting it to measurement data of SiO2 on Si.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import elli\nfrom elli.fitting import ParamsHist, fit"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Reading data\n\nWe load the data from the generated\n[NeXus file](https://manual.nexusformat.org/classes/contributed_definitions/NXellipsometry.html#nxellipsometry)\nand select the angle we want to analyse.\nYou may set the ANGLE constant to 50 or 60 to select\nother angles of incidence from the example file.\nAdditionally, we're cutting the wavelength axis to be in between 210 nm and 800 nm.\nThis is because we're using literature values for Si,\nwhich are only defined in this wavelength range.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "ANGLE = 70\npsi_delta = elli.read_nexus_psi_delta(\"SiO2onSi.ellips.nxs\").loc[ANGLE].loc[210:800]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Setting parameters\n\nAs an example we analyse an oxidation layer of SiO2 on Si.\nPrior to defining our model, we have to set the parameters we want to use.\nWe're going to use a `Cauchy model <cauchy>` for SiO2 and load the Si values from\n[literature values](https://refractiveindex.info/?shelf=main&book=Si&page=Aspnes).\nThe parameter names can be choosen freely,\nbut you have to use the exact same name in the later model definition.\nThe package uses lmfit as fitting tool and you may refer to their\n[documentation](https://lmfit.github.io/lmfit-py/parameters.html#lmfit.parameter.Parameters.add)\nfor details on parameter definition.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "params = ParamsHist()\nparams.add(\"SiO2_n0\", value=1.452, min=-100, max=100, vary=False)\nparams.add(\"SiO2_n1\", value=36.0, min=-40000, max=40000, vary=False)\nparams.add(\"SiO2_n2\", value=0, min=-40000, max=40000, vary=False)\nparams.add(\"SiO2_k0\", value=0, min=-100, max=100, vary=False)\nparams.add(\"SiO2_k1\", value=0, min=-40000, max=40000, vary=False)\nparams.add(\"SiO2_k2\", value=0, min=-40000, max=40000, vary=False)\nparams.add(\"SiO2_d\", value=20, min=0, max=40000, vary=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Load silicon dispersion from the refractiveindexinfo database\nYou can load any material from the index\n[refractiveindex.info](https://refractiveindex.info)_, which is\nembedded into the software (so you may use it offline, too). Here, we\nare interested in the literature values for the silicon substrate.\nFirst we need to load the database with ``rii_db = elli.db.RII()`` and\nthen we can query it with ``rii_db.get_mat(\"Si\", \"Aspnes\")`` to load\nthis\n[entry](https://refractiveindex.info/?shelf=main&book=Si&page=Aspnes)_.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "rii_db = elli.db.RII()\nSi = rii_db.get_mat(\"Si\", \"Aspnes\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Building the model\n\nFor simple parameter estimation,\nthe fit decorator (**@fit**) in conjuction with the model definition is used.\nThe fitting decorator takes a pandas dataframe containing\nthe psi/delta measurement data (**psi_delta**) and the model parameters (**params**) as an input.\nIt then passes the wavelength from measurement dataframe (**lbda**)\nand the parameters to the actual model function.\n\nInside the model function the optical model is built,\ni.e. the Si literature values are loaded\nand the fitting parameters are filled into the Cauchy dispersion.\nFor details on how to insert data into the Cauchy model or other optical dispersion models,\nyou may refer to the documentation of pyElli.\nPlease keep in mind that the parameters you use here\nhave to be defined in the parameter object **param**.\n\nFrom the dispersion model isotropic materials are generated\n(could also be an anisotropic material, refer to the docs for an overview).\nThis is done by calling the :code:`elli.IsotropicMaterial(...)` function\nwith a dispersion model as a parameter\nor simply calling :code:`.get_mat()` on a dispersion model.\nThese two approaches are equivalent.From these materials the layer is build,\nwhich only consists of the SiO2 layer in this example.\nThe final structure consists of an incoming half-space,\nthe layers and an outgoing half space. Specifically,\ntypically the light is coming from air and finally gets absorbed by the bulk material,\nin our example this is Si, i.e. we call :code:`elli.Structure(elli.AIR, Layer, Si)`.\n\nTo provide simulated data, we have to evaluate the structure\nby calling the :code:`evaluate(...)` function,\nwhich takes the experimental wavelength array **lbda**, **ANGLE** under which the experiment\nwas performed and the solver to be used to solve the transfer-matrix problem.\nHere, we use a simple 2x2 matrix approach,\nwhich splits the interaction in s and p-parts and therefore cannot account for anisotropy.\nThere exist 4x4 matrix solvers as well.\nYou may refer to the `solver documentation <solvers>` for further details.\n\nExecuting the cell below in a jupyter notebook displays a comparison of the simulated \u03a8 / \u0394 values\nat the current parameter values with their measured counterparts.\nAdditionally, input fields for each model parameter are shown.\nYou may change the parameters and the calcualted data will change accordingly.\nFor clarification the modeled data is shown with `_calc` postfix in the legend.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "@fit(psi_delta, params)\ndef model(lbda, params):\n    # Generate the cauchy model from the current lmfit parameters\n    SiO2 = elli.Cauchy(\n        params[\"SiO2_n0\"],\n        params[\"SiO2_n1\"],\n        params[\"SiO2_n2\"],\n        params[\"SiO2_k0\"],\n        params[\"SiO2_k1\"],\n        params[\"SiO2_k2\"],\n    ).get_mat()\n    # get_mat() generates an IsotropicMaterial from the dispersion relation\n\n    # Construct the layers you expect in your sample\n    # Here, it only consists of one layer SiO2 in between air and Si.\n    # We build the structure coming from air, through the layers,\n    # represented as an array, and having Si as bulk material.\n    structure = elli.Structure(\n        elli.AIR,  # Input medium\n        [elli.Layer(SiO2, params[\"SiO2_d\"])],  # Overlayer structure\n        Si,\n    )  # Output medium / Substrate\n\n    # The model should return the evaluation of the structure at the experimental wavelengths lbda,\n    # the experimental angle ANGLE and it should define a solver to calculate the transfer matrix.\n    return structure.evaluate(lbda, ANGLE, solver=elli.Solver2x2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Fit and plot fit results\n\nThe fit of the data can be executed by calling the fit() function on the model function,\nwhich automatically gets attached by the @fit decorator in the cell above.\nThe following cell basically executes the fit and plots\na comparison between the measurement and fitted data.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fit_stats = model.fit()\nmodel.plot()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Extracting the optical properties from the fit\n\nSince we want to extract the dispersion relation of a layer in our measured stack,\nwe can use our fitted parameters.\nThe fit parameters are contained in the fits output :code:`params` attribute,\ni.e. :code:`fit_stats.params` for our example.\nWe can use it to call our dispersion relation we used in our model\n(here it is a Cauchy dispersion relation)\nand fill in our fitted value.\nBy calling :code:`get_dielectric_df()` we can get the dielectric function of the material,\nwhich is plotted here as an example. :code:`get_dielectric_df` uses a default wavelength range\nwhich can also be changed by inputting a wavelength array as a parameter.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fitted_model = elli.Cauchy(\n    fit_stats.params[\"SiO2_n0\"],\n    fit_stats.params[\"SiO2_n1\"],\n    fit_stats.params[\"SiO2_n2\"],\n    fit_stats.params[\"SiO2_k0\"],\n    fit_stats.params[\"SiO2_k1\"],\n    fit_stats.params[\"SiO2_k2\"],\n)\n\nfitted_model.get_dielectric_df().plot(backend=\"plotly\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can also call :code:`get_refractive_index_df()`\nto get the refractive index of the material as dataframe.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fitted_model.get_refractive_index_df().plot(backend=\"plotly\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "If you want to write your data to a file you simply call pandas :code:`to_csv(...)`\nfunction to write a csv file, i.e. for the dielectric function this writes as\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fitted_model.get_dielectric_df().to_csv(\"SiO2_diel_func.csv\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "You may also access a single value of your optical model\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fit_stats.params[\"SiO2_n0\"].value"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Our simply print the fitted values in a list together with their fitting errors\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fit_stats.params"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Show fit statistics\nNow, we may also print out the fit statictics from the model fit in the cell above.\nThe fit statistics are simple [lmfit fit statistics](https://lmfit.github.io/lmfit-py/fitting.html#), too.\nTypically, one uses chi square values as a figure of merit for the fit quality.\nIt is stored in the chisqr attribute of the fit_stats variable we defined above.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fit_stats.chisqr"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can print the full fit statistics, too.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fit_stats"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## References\n[Here](https://github.com/PyEllips/pyElli/tree/master/examples/Basic%20Usage)\nyou can find the latest jupyter notebook and data files of this example.\n\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "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.8.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}