diff --git a/docs/adding_compute_funs.rst b/docs/dev_guide/adding_compute_funs.rst similarity index 99% rename from docs/adding_compute_funs.rst rename to docs/dev_guide/adding_compute_funs.rst index 67662cb571..45fd2c1400 100644 --- a/docs/adding_compute_funs.rst +++ b/docs/dev_guide/adding_compute_funs.rst @@ -1,5 +1,6 @@ +============================= Adding new physics quantities ------------------------------ +============================= .. role:: console(code) :language: console diff --git a/docs/adding_objectives.rst b/docs/dev_guide/adding_objectives.rst similarity index 94% rename from docs/adding_objectives.rst rename to docs/dev_guide/adding_objectives.rst index 2fa3028bad..c50e8ac588 100644 --- a/docs/adding_objectives.rst +++ b/docs/dev_guide/adding_objectives.rst @@ -1,5 +1,6 @@ +============================== Adding new objective functions ------------------------------- +============================== This guide walks through creating a new objective to optimize using Quasi-symmetry as an example. The primary methods needed for a new objective are ``__init__``, ``build``, @@ -76,6 +77,7 @@ A full example objective with comments describing the key points is given below: jac_chunk_size=None, ): # we don't have to do much here, mostly just call ``super().__init__()`` + # to inherit common initialization logic from ``desc.objectives._Objective`` if target is None and bounds is None: target = 0 # default target value self._grid = grid @@ -111,9 +113,13 @@ A full example objective with comments describing the key points is given below: else: grid = self._grid # dim_f = size of the output vector returned by self.compute - # usually the same as self.grid.num_nodes, unless you're doing some downsampling - # or averaging etc. - self._dim_f = self.grid.num_nodes + # self.compute refers to the objective's own compute method + # Typically an objective returns the output of a quantity computed in + # ``desc.compute``, with some additional scale factor. + # In these cases dim_f should match the size of the quantity calculated in + # ``desc.compute`` (for example self.grid.num_nodes). + # If the objective does post-processing on the quantity, like downsampling or + # averaging, then dim_f should be changed accordingly. # What data from desc.compute is needed? Here we want the QS triple product. self._data_keys = ["f_T"] diff --git a/docs/adding_optimizers.rst b/docs/dev_guide/adding_optimizers.rst similarity index 99% rename from docs/adding_optimizers.rst rename to docs/dev_guide/adding_optimizers.rst index 1568bf0377..940bc87cbf 100644 --- a/docs/adding_optimizers.rst +++ b/docs/dev_guide/adding_optimizers.rst @@ -1,7 +1,8 @@ .. _adding-optimizers: +===================== Adding new optimizers ----------------------- +===================== This guide walks through adding an interface to a new optimizer. As an example, we will write an interface to the popular open source ``ipopt`` interior point method. diff --git a/docs/dev_guide/backend.rst b/docs/dev_guide/backend.rst new file mode 100644 index 0000000000..f6d8b24eb8 --- /dev/null +++ b/docs/dev_guide/backend.rst @@ -0,0 +1,24 @@ +======= +Backend +======= + + +DESC uses JAX for faster compile times, automatic differentiation, and other scientific computing tools. +The purpose of ``backend.py`` is to determine whether DESC may take advantage of JAX and GPUs or default to standard ``numpy`` and CPUs. + +JAX provides a ``numpy`` style API for array operations. +In many cases, to take advantage of JAX, one only needs to replace calls to ``numpy`` with calls to ``jax.numpy``. +A convenient way to do this is with the import statement ``import jax.numpy as jnp``. + +Of course if such an import statement is used in DESC, and DESC is run on a machine where JAX is not installed, then a runtime error is thrown. +We would prefer if DESC still works on machines where JAX is not installed. +With that goal, in functions which can benefit from JAX, we use the following import statement: ``from desc.backend import jnp``. +``desc.backend.jnp`` is an alias to ``jax.numpy`` if JAX is installed and ``numpy`` otherwise. + +While ``jax.numpy`` attempts to serve as a drop in replacement for ``numpy``, it imposes some constraints on how the code is written. +For example, ``jax.numpy`` arrays are immutable. +This means in-place updates to elements in arrays is not possible. +To update elements in ``jax.numpy`` arrays, memory needs to be allocated to create a new array with the updated element. +Similarly, JAX's JIT compilation requires code flow structures such as loops and conditionals to be written in a specific way. + +The utility functions in ``desc.backend`` provide a simple interface to perform these operations. diff --git a/docs/dev_guide/compute.rst b/docs/dev_guide/compute.rst new file mode 100644 index 0000000000..8149fa407e --- /dev/null +++ b/docs/dev_guide/compute.rst @@ -0,0 +1,96 @@ +============================= +Adding new physics quantities +============================= + + +All calculation of physics quantities takes place in ``desc.compute`` + +As an example, we'll walk through the calculation of the radial component of the MHD +force :math:`F_\rho` + +The full code is below: +:: + + from desc.data_index import register_compute_fun + + @register_compute_fun( + name="F_rho", + label="F_{\\rho}", + units="N \\cdot m^{-2}", + units_long="Newtons / square meter", + description="Covariant radial component of force balance error", + dim=1, + params=[], + transforms={}, + profiles=[], + coordinates="rtz", + data=["p_r", "sqrt(g)", "B^theta", "B^zeta", "J^theta", "J^zeta"], + ) + def _F_rho(params, transforms, profiles, data, **kwargs): + data["F_rho"] = -data["p_r"] + data["sqrt(g)"] * ( + data["B^zeta"] * data["J^theta"] - data["B^theta"] * data["J^zeta"] + ) + return data + +The decorator ``register_compute_fun`` tells DESC that the quantity exists and contains +metadata about the quantity. The necessary fields are detailed below: + + +* ``name``: A short, meaningful name that is used elsewhere in the code to refer to the + quantity. This name will appear in the ``data`` dictionary returned by ``Equilibrium.compute``, + and is also the argument passed to ``compute`` to calculate the quantity. IE, + ``Equilibrium.compute("F_rho")`` will return a dictionary containing ``F_rho`` as well + as all the intermediate quantities needed to compute it. General conventions are that + covariant components of a vector are called ``X_rho`` etc, contravariant components + ``X^rho`` etc, and derivatives by a single character subscript, ``X_r`` etc for :math:`\partial_{\rho} X` +* ``label``: a LaTeX style label used for plotting. +* ``units``: SI units of the quantity in LaTeX format +* ``units_long``: SI units of the quantity, spelled out +* ``description``: A short description of the quantity +* ``dim``: Dimensionality of the quantity. Vectors (such as magnetic field) have ``dim=3``, + local scalar quantities (such as vector components, pressure, volume element, etc) + have ``dim=1``, global scalars (such as total volume, aspect ratio, etc) have ``dim=0`` +* ``params``: list of strings of ``Equilibrium`` parameters needed to compute the quantity + such as ``R_lmn``, ``Z_lmn`` etc. These will be passed into the compute function as a + dictionary in the ``params`` argument. Note that this only includes direct dependencies + (things that are used in this function). For most quantities, this will be an empty list. + For example, if the function relies on ``R_t``, this dependency should be specified as a + data dependency (see below), while the function to compute ``R_t`` itself will depend on + ``R_lmn`` +* ``transforms``: a dictionary of what ``transform`` objects are needed, with keys being the + quantity being transformed (``R``, ``p``, etc) and the values are a list of derivative + orders needed in ``rho``, ``theta``, ``zeta``. IE, if the quantity requires + :math:`R_{\rho}{\zeta}{\zeta}`, then ``transforms`` should be ``{"R": [[1, 0, 2]]}`` + indicating a first derivative in ``rho`` and a second derivative in ``zeta``. Note that + this only includes direct dependencies (things that are used in this function). For most + quantities this will be an empty dictionary. +* ``profiles``: List of string of ``Profile`` quantities needed, such as ``pressure`` or + ``iota``. Note that this only includes direct dependencies (things that are used in + this function). For most quantities this will be an empty list. +* ``coordinates``: String denoting which coordinate the quantity depends on. Most will be + ``"rtz"`` indicating it is a function of :math:`\rho, \theta, \zeta`. Profiles and flux surface + quantities would have ``coordinates="r"`` indicating it only depends on `:math:\rho` +* ``data``: What other physics quantities are needed to compute this quantity. In our + example, we need the radial derivative of pressure ``p_r``, the Jacobian determinant + ``sqrt(g)``, and contravariant components of current and magnetic field. These dependencies + will be passed to the compute function as a dictionary in the ``data`` argument. Note + that this only includes direct dependencies (things that are used in this function). + For example, we need ``sqrt(g)``, which itself depends on ``e_rho``, etc. But we don't + need to specify ``e_rho`` here, that dependency is determined automatically at runtime. +* ``kwargs``: If the compute function requires any additional arguments they should + be specified like ``kwarg="thing"`` where the value is the name of the keyword argument + that will be passed to the compute function. Most quantities do not take kwargs. + + +The function itself should have a signature of the form +:: + + _foo(params, transforms, profiles, data, **kwargs) + +Our convention is to start the function name with an underscore and have it be +something like the ``name`` attribute, but the name of the function doesn't actually matter +as long as it is registered. +``params``, ``transforms``, ``profiles``, and ``data`` are dictionaries containing the needed +dependencies specified by the decorator. The function itself should do any calculation +needed using these dependencies and then add the output to the ``data`` dictionary and +return it. The key in the ``data`` dictionary should match the ``name`` of the quantity. diff --git a/docs/dev_guide/compute_2.ipynb b/docs/dev_guide/compute_2.ipynb new file mode 100644 index 0000000000..afc4ca3ad1 --- /dev/null +++ b/docs/dev_guide/compute_2.ipynb @@ -0,0 +1,218 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "17782242-c991-484d-bb46-811952ee9c38", + "metadata": {}, + "source": [ + "# `coils.py`" + ] + }, + { + "cell_type": "markdown", + "id": "febe173a-38c4-44ec-ba2f-6d556b22dd50", + "metadata": { + "tags": [] + }, + "source": [ + "## Introduction" + ] + }, + { + "cell_type": "markdown", + "id": "4848dc72-9eaf-41cf-9937-aa937287b901", + "metadata": { + "tags": [] + }, + "source": [ + "In DESC, quantities of interest (such as force error `F`, magnetic field strength `|B|` and its vector components) are computed from `Equilibrium` objects through the use of `compute_XXX` functions. The `Equilibrium` object has a `eq.compute(name=\"NAME OF QUANTITY TO COMPUTE\",grid=Grid)` method which takes in the string `name` of the quantity to compute from the `Equilibrium`, and a `Grid` of coordinates $(\\rho,\\theta,\\zeta)$ to evaluate the quantity at. This method then uses the `name` to find which `compute_XXX` function is needed to call in order to calculate that quantity.\n", + "\n", + "These `compute_XXX` functions live inside of the `desc/compute` folder, and inside that folder the `data_index.py` file contains the list of all available quantities that `name` could specify to be computed, as well as information on that quantity and which `compute_XXX` function should be called in order to compute it.\n", + "\n", + "The `compute_XXX` functions have function signatures that take in as arguments the necessary variables from `{R_lmn, Z_lmn, L_lmn, i_l, p_l,Psi}` required to calculate the quantity, as well as the `Transform` and/or `Profile` objects needed to evaluate those variables (which are spectral coefficients, except for `Psi`) at the points in real space specified by the desired `Grid` object (which is contained in the `Transform` objects passed).\n", + "\n", + "\n", + "An example compute function from `_field.py` is shown here for computing the magnetic field magnitude:\n", + "```python\n", + "def compute_magnetic_field_magnitude(\n", + " R_lmn,\n", + " Z_lmn,\n", + " L_lmn,\n", + " i_l,\n", + " Psi,\n", + " R_transform,\n", + " Z_transform,\n", + " L_transform,\n", + " iota,\n", + " data=None,\n", + " **kwargs,\n", + "):\n", + "```\n", + "The first 3 arguments `R_lmn,Z_lmn,L_lmn` are the Fourier-Zernike spectral coefficients of the toroidal coordinates of the flux surfaces `R,Z`, and of the poloidal stream function $\\lambda$. `i_l` is the coefficients of the rotational transform profile (typically a power series in `rho`). `Psi` is the enclosed toroidal flux.\n", + "\n", + "The `_transform` arguments are `Transform` objects which transform the spectral coefficients to their values in real-space. `iota` is a `Profile` object which transforms `i_l` into its values in real-space.\n", + "The `data` argument is an optional dictionary. The compute functions store the quantities they calculate in a `data` dictionary and return it, and if `data` is passed in, the quantities this function computes will be added to this dictionary. This way, a compute function can add on to the quantities already calculated by previous compute functions, or it can use other compute functions to calculate preliminary quantities to avoid duplicating code. As an example, in the `compute_magnetic_field_magnitude` function, the contravariant components of the magnetic field $B^i$ are required, along with the metric coefficients $g_ij$. To calculate these, the function calls:\n", + "\n", + "```python\n", + "data = compute_contravariant_magnetic_field(\n", + " R_lmn,\n", + " Z_lmn,\n", + " L_lmn,\n", + " i_l,\n", + " Psi,\n", + " R_transform,\n", + " Z_transform,\n", + " L_transform,\n", + " iota,\n", + " data=data,\n", + ")\n", + "data = compute_covariant_metric_coefficients(\n", + " R_lmn, Z_lmn, R_transform, Z_transform, data=data\n", + ")\n", + "```\n", + "\n", + "in order to populate `data` with these necessary preliminary quantities.\n", + "\n", + "\n", + "- talk about what the data arg is and how it is used\n", + "- maybe include example of how to make your own (let's say for a stupid thing like B_theta * B_zeta)\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "f4737eaf-3f27-425a-a50d-2e439e2bdb91", + "metadata": { + "tags": [] + }, + "source": [ + "## Calculating Quantities" + ] + }, + { + "cell_type": "markdown", + "id": "d613b5af-21b0-4a4c-be45-181b81ab0c0b", + "metadata": {}, + "source": [ + "Inside the compute function, every quantity is stored inside the `data` dictionary under the key of the name of the quantity. \n", + "As an example, `data['|B|']` contains the magnetic field magnitude.\n", + "This quantity is stored as a JAX array of size `(num_nodes,)` (if the quantity is NOT a vector), or `(num_nodes,3)` (if the quantity is a vector, i.e. `data['B']`, which contains all three components $[B_R, B_{\\phi},B_{Z}]$ of $B$ at each node) (`num_nodes` is the number of nodes in the `Grid` object that the quantity was computed on. Can be accessed by `grid.num_nodes`). \n", + "This array is flattened, so if the grid used has 10 equispaced gridpoints in $(\\rho,\\theta,\\zeta)$, the grid will have $10^3 = 1000$ nodes, and any quantity calculated on that grid will be returned as an array of size `(num_nodes,)` if not a vector and `(num_nodes,3)` if a vector quantity.\n", + "### Scalar Algebra\n", + "Storing the quantities in arrays like this enables for easy computation using these quantities. For example, if one wants to calculate the magnitude of the pressure gradient $|\\nabla p(\\rho)| = \\sqrt{p'(\\rho)^2}|\\nabla\\rho|$, one simply [writes out the expression](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_equil.py#L114) after calling the necessary compute functions:\n", + "\n", + "```python\n", + "data[\"|grad(p)|\"] = jnp.sqrt(data[\"p_r\"] ** 2) * data[\"|grad(rho)|\"]\n", + "```\n", + "\n", + "### Vector Algebra\n", + "If calculating a quantity which involves vector algebra, the format of these arrays makes it simple to write out as well. As an example, if calculating the contravariant radial basis vector $\\mathbf{e}^{\\rho} = \\frac{\\mathbf{e}^{\\theta} \\times \\mathbf{e}^{\\zeta}}{\\sqrt{g}}$, one [writes](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_core.py#L426):\n", + "```python\n", + "data[\"e^rho\"] = (cross(data[\"e_theta\"], data[\"e_zeta\"]).T / data[\"sqrt(g)\"]).T\n", + "```\n", + "Note here that once the quantities are crossed, they are transposed. This is done to ensure that the result retains the desired shape of `(num_nodes,3)`.\n", + "\n", + "### Be Mindful of Shapes\n", + "\n", + "It is important to keep in mind the shapes of the quantities being manipulated to ensure the desired operation is carried out. As another example, the gradient of the magnetic toroidal flux $\\nabla \\psi = \\frac{d\\psi}{d\\rho}\\nabla \\rho$ is [calculated as](https://github.com/PlasmaControl/DESC/blob/94d7e43542613b1c901fcd655502312f3e567c26/desc/compute/_core.py#L701):\n", + "```python\n", + "data[\"grad(psi)\"] = (data[\"psi_r\"] * data[\"e^rho\"].T).T\n", + "```\n", + "The desired operation here is to multiply `data[\"psi_r\"]`, which is a scalar quantity at each grid point and so is of shape `(num_nodes,)` with `data[\"e^rho\"]`, a vector quantity and so is of shape `(num_nodes,3)`. \n", + "We want the result to be of shape `(num_nodes,3)`. \n", + "In order to do so, we first must transpose the vector quantity to be shape `(3,num_nodes)`, so that when multiplied together with the scalar quantity of shape `(num_nodes,3)`, the result is broadcast to an array of shape `(3,num_nodes)`. \n", + "If the transpose did not occur, the two shapes `(num_nodes,)` and `(num_nodes,3)` would be incompatible with eachother.\n", + "The second transpose after the multiplication is to ensure that the result is in the shape `(num_nodes,3)`, as is the convention expected in the code." + ] + }, + { + "cell_type": "markdown", + "id": "8c765ae4-ea52-4360-92a4-e91126efc51f", + "metadata": { + "tags": [] + }, + "source": [ + "## What `check_derivs()` does" + ] + }, + { + "cell_type": "markdown", + "id": "9f2c39bb-2bc3-413c-b2c3-428619da1faa", + "metadata": {}, + "source": [ + "Basically, this function ensures that the transforms passed to the compute function have the necessary derivatives of $R,Z,L,p,\\iota$ to calculate the quantity contained in the if statement. \n", + "If yes, it returns `True` and the quantity in the logival is computed. If not, it returns `False` and that quantitiy is not calculated. \n", + "This allows us to call a function to get a specific quantity which may not need high order derivatives, and avoid needing to compute those derivatives anyways just to have the function call not throw an error that the necessary derivatives do not exist for a quantitiy we are not asking for but which needs higher order derivatives to compute" + ] + }, + { + "cell_type": "markdown", + "id": "21ec3f84-f929-4757-a5de-47824e50acd9", + "metadata": { + "tags": [] + }, + "source": [ + "## `__init__.py`" + ] + }, + { + "cell_type": "markdown", + "id": "4efca99b-2587-4fb8-bd26-20d1299a365e", + "metadata": {}, + "source": [ + "`arg_order` is defined in this file. This tuple is used in other parts of the code to determine how to parse the state vector `x` into the various arguments that make it up, and also for making the derivatives of functions of these arguments, such as inside of `_set_derivatives` method of `_Objective` in `objective_funs.py`." + ] + }, + { + "cell_type": "markdown", + "id": "d96ae667-54e3-4434-aa5f-11a4e00b250b", + "metadata": { + "tags": [] + }, + "source": [ + "## `compute/utils.py`" + ] + }, + { + "cell_type": "markdown", + "id": "e304bafa-cd0a-4438-b181-037ba316aee3", + "metadata": {}, + "source": [ + " - dot\n", + " - cross\n", + " custom vector algebra fxns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a1f7820-a65b-4fb8-8ccc-1a61f1c50e9a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/compute_notebook.ipynb b/docs/dev_guide/compute_notebook.ipynb new file mode 100644 index 0000000000..defe423423 --- /dev/null +++ b/docs/dev_guide/compute_notebook.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5185c760-63c6-42b8-8a5a-ce6eaebdbd52", + "metadata": { + "tags": [], + "toc-hr-collapsed": true + }, + "source": [ + "# How compute works?" + ] + }, + { + "cell_type": "markdown", + "id": "febe173a-38c4-44ec-ba2f-6d556b22dd50", + "metadata": { + "tags": [] + }, + "source": [ + "## Introduction" + ] + }, + { + "cell_type": "markdown", + "id": "4848dc72-9eaf-41cf-9937-aa937287b901", + "metadata": { + "tags": [] + }, + "source": [ + "In DESC, quantities of interest (such as force error `F`, magnetic field strength `|B|` and its vector components) are computed from `Equilibrium` objects through the use of `compute_XXX` functions. The `Equilibrium` object has a `eq.compute(name=\"NAME OF QUANTITY TO COMPUTE\",grid=Grid)` method which takes in the string `name` of the quantity to compute from the `Equilibrium`, and a `Grid` of coordinates $(\\rho,\\theta,\\zeta)$ to evaluate the quantity at. This method then uses the `name` to find which `compute_XXX` function is needed to call in order to calculate that quantity.\n", + "\n", + "These `compute_XXX` functions live inside of the `desc/compute` folder, and inside that folder the `data_index.py` file contains the list of all available quantities that `name` could specify to be computed, as well as information on that quantity and which `compute_XXX` function should be called in order to compute it.\n", + "\n", + "The `compute_XXX` functions have function signatures that take in as arguments the necessary variables from `{R_lmn, Z_lmn, L_lmn, i_l, c_l, p_l,Psi}` required to calculate the quantity contained in a dict argument named `params`, as well as the `Transform` objects (in the `transforms` dict argument) and/or `Profile` objects (in the `profiles` dict argument) needed to evaluate those variables in `params` (which are spectral coefficients, except for `Psi`) at the points in real space specified by the desired `Grid` object (which is contained in the `Transform` objects passed).\n", + "\n", + "\n", + "An example compute function from `_field.py` is shown here for computing the magnetic field magnitude:\n", + "```python\n", + "def compute_magnetic_field_magnitude(\n", + " params,\n", + " transforms,\n", + " profiles,\n", + " data=None,\n", + " **kwargs,\n", + "):\n", + "```\n", + "Every compute function in DESC has the same function signature:\n", + "\n", + " - `params` is a dict of the basic parameters needed to compute data, i.e. `{R_lmn, Z_lmn, L_lmn, i_l, c_l p_l,Psi}`\n", + " - The possible params are: \n", + " - `R_lmn Z_lmn, L_lmn` the Fourier-Zernike spectral coeffiiencts describing the toroidal coordinates R and Z of the flux surfaces and the poloidal stream function $\\lambda$ (`L_lmn`).\n", + " - `i_l, c_l, p_l` the parameters (either spectral coefficients for a `PowerSeriesProfile` or spline values for a `SplineProfile` ) for the profiles of rotational transform (`i_l`), net enclosed toroidal current (`c_l`) and pressure (`p_l`). Note that only one of `i_l,c_l` are needed, and if both are passed the rotational transform `i_l` will be used.\n", + " - `Psi` is the total enclosed toroidal flux, a scalar, in Wb.\n", + " - `transforms` is a dict of the transforms (`Transform` objects) needed to transform the spectral coefficients from `params` to their values in real space.\n", + " - `profiles` is a dict of the profiles (`Profile` objects) needed to evaluate the radial profiles of pressure, rotational transform and net enclosed toroidal current\n", + " - `data` argument is an optional dictionary. The compute functions store the quantities they calculate in a `data` dictionary and return it, and if `data` is passed in, the quantities this function computes will be added to this dictionary. This way, a compute function can add on to the quantities already calculated by previous compute functions, or it can use other compute functions to calculate preliminary quantities to avoid duplicating code.\n", + "\n", + "The first 3 arguments `R_lmn,Z_lmn,L_lmn` are the Fourier-Zernike spectral coefficients of the toroidal coordinates of the flux surfaces `R,Z`, and of the poloidal stream function $\\lambda$. `i_l` is the coefficients of the rotational transform profile (typically a power series in `rho`). `Psi` is the enclosed toroidal flux.\n", + "\n", + "The `_transform` arguments are `Transform` objects which transform the spectral coefficients to their values in real-space. `iota` is a `Profile` object which transforms `i_l` into its values in real-space.\n", + "As an example, in the `compute_magnetic_field_magnitude` function, the contravariant components of the magnetic field $B^i$ are required, along with the metric coefficients $g_ij$. To calculate these, the function calls:\n", + "\n", + "```python\n", + " data = compute_contravariant_magnetic_field(\n", + " params,\n", + " transforms,\n", + " profiles,\n", + " data=data,\n", + " **kwargs,\n", + " )\n", + " data = compute_covariant_metric_coefficients(\n", + " params,\n", + " transforms,\n", + " profiles,\n", + " data=data,\n", + " **kwargs,\n", + " )\n", + "```\n", + "\n", + "in order to populate `data` with these necessary preliminary quantities.\n", + "\n", + "\n", + "- maybe include example of how to make your own (let's say for a simple thing like B_theta * B_zeta)\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "f4737eaf-3f27-425a-a50d-2e439e2bdb91", + "metadata": { + "tags": [] + }, + "source": [ + "## Calculating Quantities" + ] + }, + { + "cell_type": "markdown", + "id": "d613b5af-21b0-4a4c-be45-181b81ab0c0b", + "metadata": {}, + "source": [ + "Inside the compute function, every quantity is stored inside the `data` dictionary under the key of the name of the quantity. \n", + "As an example, `data['|B|']` contains the magnetic field magnitude.\n", + "This quantity is stored as a JAX array of size `(num_nodes,)` (if the quantity is NOT a vector), or `(num_nodes,3)` (if the quantity is a vector, i.e. `data['B']`, which contains all three components $[B_R, B_{\\phi},B_{Z}]$ of $B$ at each node) (`num_nodes` is the number of nodes in the `Grid` object that the quantity was computed on. Can be accessed by `grid.num_nodes`). \n", + "This array is flattened, so if the grid used has 10 equispaced gridpoints in $(\\rho,\\theta,\\zeta)$, the grid will have $10^3 = 1000$ nodes, and any quantity calculated on that grid will be returned as an array of size `(num_nodes,)` if not a vector and `(num_nodes,3)` if a vector quantity.\n", + "### Scalar Algebra\n", + "Storing the quantities in arrays like this enables for easy computation using these quantities. For example, if one wants to calculate the magnitude of the pressure gradient $|\\nabla p(\\rho)| = \\sqrt{p'(\\rho)^2}|\\nabla\\rho|$, one simply [writes out the expression](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_equil.py#L114) after calling the necessary compute functions:\n", + "\n", + "```python\n", + "data[\"|grad(p)|\"] = jnp.sqrt(data[\"p_r\"] ** 2) * data[\"|grad(rho)|\"]\n", + "```\n", + "\n", + "### Vector Algebra\n", + "If calculating a quantity which involves vector algebra, the format of these arrays makes it simple to write out as well. As an example, if calculating the contravariant radial basis vector $\\mathbf{e}^{\\rho} = \\frac{\\mathbf{e}^{\\theta} \\times \\mathbf{e}^{\\zeta}}{\\sqrt{g}}$, one [writes](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_core.py#L426):\n", + "```python\n", + "data[\"e^rho\"] = (cross(data[\"e_theta\"], data[\"e_zeta\"]).T / data[\"sqrt(g)\"]).T\n", + "```\n", + "Note here that once the quantities are crossed, they are transposed. This is done to ensure that the result retains the desired shape of `(num_nodes,3)`.\n", + "\n", + "### Be Mindful of Shapes\n", + "\n", + "It is important to keep in mind the shapes of the quantities being manipulated to ensure the desired operation is carried out. As another example, the gradient of the magnetic toroidal flux $\\nabla \\psi = \\frac{d\\psi}{d\\rho}\\nabla \\rho$ is [calculated as](https://github.com/PlasmaControl/DESC/blob/94d7e43542613b1c901fcd655502312f3e567c26/desc/compute/_core.py#L701):\n", + "```python\n", + "data[\"grad(psi)\"] = (data[\"psi_r\"] * data[\"e^rho\"].T).T\n", + "```\n", + "The desired operation here is to multiply `data[\"psi_r\"]`, which is a scalar quantity at each grid point and so is of shape `(num_nodes,)` with `data[\"e^rho\"]`, a vector quantity and so is of shape `(num_nodes,3)`. \n", + "We want the result to be of shape `(num_nodes,3)`. \n", + "In order to do so, we first must transpose the vector quantity to be shape `(3,num_nodes)`, so that when multiplied together with the scalar quantity of shape `(num_nodes,3)`, the result is broadcast to an array of shape `(3,num_nodes)`. \n", + "If the transpose did not occur, the two shapes `(num_nodes,)` and `(num_nodes,3)` would be incompatible with eachother.\n", + "The second transpose after the multiplication is to ensure that the result is in the shape `(num_nodes,3)`, as is the convention expected in the code." + ] + }, + { + "cell_type": "markdown", + "id": "8c765ae4-ea52-4360-92a4-e91126efc51f", + "metadata": { + "tags": [] + }, + "source": [ + "## What `check_derivs()` does" + ] + }, + { + "cell_type": "markdown", + "id": "9f2c39bb-2bc3-413c-b2c3-428619da1faa", + "metadata": {}, + "source": [ + "Basically, this function ensures that the transforms passed to the compute function have the necessary derivatives of $R,Z,L,p,\\iota$ to calculate the quantity contained in the if statement. \n", + "If yes, it returns `True` and the quantity in the logival is computed. If not, it returns `False` and that quantitiy is not calculated. \n", + "This allows us to call a function to get a specific quantity which may not need high order derivatives, and avoid needing to compute those derivatives anyways just to have the function call not throw an error that the necessary derivatives do not exist for a quantitiy we are not asking for but which needs higher order derivatives to compute" + ] + }, + { + "cell_type": "markdown", + "id": "21ec3f84-f929-4757-a5de-47824e50acd9", + "metadata": { + "tags": [] + }, + "source": [ + "## `__init__.py`" + ] + }, + { + "cell_type": "markdown", + "id": "4efca99b-2587-4fb8-bd26-20d1299a365e", + "metadata": {}, + "source": [ + "`arg_order` is defined in this file. This tuple is used in other parts of the code to determine how to parse the state vector `x` into the various arguments that make it up, and also for making the derivatives of functions of these arguments, such as inside of `_set_derivatives` method of `_Objective` in `objective_funs.py`." + ] + }, + { + "cell_type": "markdown", + "id": "e06c0cc5-cc42-4b08-a7b0-87b91bb62970", + "metadata": {}, + "source": [ + "why does arg_order exist again? It is so we can check if things have the necessary arguments?\n", + "\n", + "we need canonical ordering of things so when we combine all the args into x and all the constraints into A everything lines up correctly. We also use it in some places for a shorthand of all the args that could be used by any objective, but i think in those cases we only ever need to know about args that are taken by the objectives at hand, so we could just use that" + ] + }, + { + "cell_type": "markdown", + "id": "d96ae667-54e3-4434-aa5f-11a4e00b250b", + "metadata": { + "tags": [] + }, + "source": [ + "## `compute/utils.py`" + ] + }, + { + "cell_type": "markdown", + "id": "e304bafa-cd0a-4438-b181-037ba316aee3", + "metadata": {}, + "source": [ + " - dot\n", + " - cross\n", + " custom vector algebra fxns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a1f7820-a65b-4fb8-8ccc-1a61f1c50e9a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/configuration_equilibrium.rst b/docs/dev_guide/configuration_equilibrium.rst new file mode 100644 index 0000000000..5e606563fd --- /dev/null +++ b/docs/dev_guide/configuration_equilibrium.rst @@ -0,0 +1,40 @@ +=================================== +Configuration.py and Equilibrium.py +=================================== + +To construct an equilibrium, the relevant parameters that decide the plasma state need to created and passed into the constructor of the ``Equilibrium`` class. +See ``Initializing an Equilibrium`` for a walk-through of this process. + +These parameters are then automatically passed into the ``Configuration`` class, which is the abstract base class for equilibrium objects. +Almost all the work to initialize an equilibrium object is done in ``configuration.py``, while ``equilibrium.py`` serves as a wrapper class with methods that call routines to solve and optimize the equilibrium. + +The attributes of a ``Configuration`` object can be organized into three groups. + +The first group has parameters relavant to generating the basis functions based on the specified device parameters like the number of field periods and grid parameters that determine resolution and type of spectral indexing of ``ansi`` or ``fringe``. + +The second group of attributes are related to device geometry. +The ``surface`` of an equilbrium specifies the plasma boundary condition in terms of either parameterizing the last closed flux surface with a ``FourierRZToroidalSurface`` or a poincare cross-section of the toroidal coordinate with a ``ZernikeRZToroidalSection``. +An initial guess for the magnetic axis can help the equilibrium solver find better equilbria quicker. +This can be specified with a ``Curve`` object as the parameter for the ``axis`` field of the equilibrium constructor. +If the magenetic axis is not specified, then the center of the surface is used as the initial guess. + +The third group of attributes are profile quantities such as pressure, rotational transform, toroidal current, and kinetic profiles. +The pressure profile and rotational transform are required to specify the plasma state. +If the pressure profile is not known kinetic profiles can be given instead. +Similarly, if the rotational transform is not known, the toroidal current profile can be given instead. + +The purpose of the ``__init__`` method is to assign these attributes while ensuring that the equilibrium is nether underdetermined (missing parameters) nor overdetermined (too many conflicting parameters, e.g. pressure and kinetic profiles). + +Once an equilbrium is initialized, it can be solved with ``equilibrium.solve()`` and later optimized with ``equilbrium.optimize()``. +Each of these methods starts an optimization routine to either minimize the force balance residual errors or a some other specified objective function. +T``Configuration`` class also contains the methods to compute quantities on the equilbrium. + +Once an equilibrium is optimized, we can compute quantities on this equilbrium with ``equilibrium.compute(names=names, grid=grid)`` where ``names`` is a list of strings that denote the names of the quantities as discussed in ``Adding new physics quantities``. +This method calls the ``compute`` method in ``Configuration.py``. + +Some quantities require certain grids to ensure they computed accurately. +In particular, quantities which rely on surface averages operations should use grids that span the entire surface evenly. +Many profiles are functions of the flux surface label and likely to rely on a surface average operation. +Similarly, volume averages are global quantities that should be computed on a quadrature grid to exactly integrate the Fourier-Zernike basis functions. +Hence, regardless of the grid specified by the user, if a flux surface function or a global quantity is a dependency of the specified parameter to be computed, these dependencies are first computed on ``LinearGrid`` and ``QuadratureGrid``, respectively. +The arrays which store these quantities are then manipulated to be broadcastable with quantities computed on the grid specified by the user and passed in as dependencies to the compute functions. diff --git a/docs/dev_guide/grid.ipynb b/docs/dev_guide/grid.ipynb new file mode 100644 index 0000000000..149d1e65cc --- /dev/null +++ b/docs/dev_guide/grid.ipynb @@ -0,0 +1,1519 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c9f37994-6783-4420-ba9e-9022fba036dd", + "metadata": {}, + "source": [ + "# Collocation grids\n", + "\n", + "The grid discretizes the computational domain into collocation nodes.\n", + "\n", + "Likely all the documentation concerning these grids relevant for non-developer users can be found [here](https://desc-docs.readthedocs.io/en/latest/notebooks/basis_grid.html#Collocation-grids).\n", + "\n", + "The purpose of this notebook is to clarify design choices in `grid.py`.\n", + "This will let new developers learn and maintain the code faster." + ] + }, + { + "cell_type": "markdown", + "id": "3c115859-2c06-47b2-9163-ce1b6d912662", + "metadata": {}, + "source": [ + "## The grid has 2 jobs.\n", + "\n", + "- Node placement\n", + "- Node weighting\n", + " - Node volume\n", + " - Node areas\n", + " - Node thickness / lengths\n", + "\n", + "This guide will first discuss these two topics in detail.\n", + "Then it will show an example of a common operation which relies on node weights.\n", + "This will provide a necessary background before we discuss implementation details." + ] + }, + { + "cell_type": "markdown", + "id": "2907d38a-f96e-4f69-9d92-518bbc110979", + "metadata": {}, + "source": [ + "## Node placement\n", + "\n", + "To begin, the user can choose from three grid types: `LinearGrid`, `QuadratureGrid`, and `ConcentricGrid`.\n", + "There is also functionality to allow the user to choose how to discretize the computational domain with a grid which has a custom set of nodes.\n", + "\n", + "One difference between the three predefined grids is the placement of nodes.\n", + "All the predefined grids linearly space each $\\theta$ and $\\zeta$ surface.\n", + "That is, on any given $\\zeta$ surface, all the nodes which lie on the same $\\rho$ surface are evenly spaced.\n", + "On any given $\\theta$ surface, all the nodes which lie on the same $\\rho$ surface are evenly spaced.\n", + "`LinearGrid`s in particular, also linearly spaces the $\\rho$ surfaces.1\n", + "As the nodes are evenly spaced in all coordinates, each node occupies the same volume in the domain.\n", + "\n", + "See the $\\zeta$ cross sections below as a visual.\n", + "\n", + "Footnote [1]: If an array is given as input for the `rho` parameter, then sometimes the `rho` surfaces are not given equal $d\\rho$.\n", + "This will be discussed later in the guide." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8680d734-8ef6-4dbc-b016-3abebe3faa1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DESC version 0.7.2+200.g861de40d,using JAX backend, jax version=0.4.1, jaxlib version=0.4.1, dtype=float64\n", + "Using device: CPU, with 11.18 GB available memory\n" + ] + } + ], + "source": [ + "import sys\n", + "import os\n", + "\n", + "sys.path.insert(0, os.path.abspath(\".\"))\n", + "sys.path.append(os.path.abspath(\"../../\"))\n", + "\n", + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "from desc.equilibrium import Equilibrium\n", + "from desc.examples import get\n", + "from desc.grid import *\n", + "from desc.plotting import plot_grid\n", + "\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "np.set_printoptions(precision=3, floatmode=\"fixed\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "de2a2946-0c6a-480f-ae44-85c40a9a1a55", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(
,\n", + " )" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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/LH+NpFIhJdeemla101KSoJKpYJUKoWDgwPKysqwffv2aV1DpVLpn6+goCC8++67+OKLL/R+JyUlwcLCYsR9ZuIrIWTEh7ehoQFxcXH63x8/fhyBgYHXrGqys7OxZcsWAMDXX3+NhIQEuLq6QiQSAQDq6+tx+fJlREVF4dNPP8XWrVshEAjg7OyMt956C/fddx96enquuS0sLEy/owGuvCZHjhzB9u3bcebMGbz88svXPIYzZ87gmWeeAQBUVVXB2toaQqEQBw4cwM6dO0f87alTp+Dr6zsiyZKdnY3U1FQcO3YM0dHR8PPzw4EDB3Dx4kV88skneOKJJya0kZCQMKbPjo6O4z4fO3fuHPfxmCRc7+Unory8nNx6660EAFm+fDm59dZb9T8pKSnExcWF+Pv7E6FQSLZt20YIIeTLL78kISEhxMrKimzfvp20t7eThIQEAoAkJCSQ6upqIhaLyZ133klWrVpF0tLSyObNm0lTU5Pe7uuvv078/PzIunXryK9//WsSFRVF/P39yZtvvjni+qmpqaSoqGiEz2KxmDz22GMkPj6epKSkkOjoaJKYmEj27dtHBgYGrvHx6mu8+eab1zweQgiprq4mqampJC4ujqxfv57s2bOHACCpqamkpaWFvPPOOyQ6Onrc5zE/P588/fTThM/nk1/96ldkz5495De/+Q0JDAwkjz76qEFeqwsXLpAnnniCvPzyy2T37t36x0oImdC/wsJCcu+995LAwEDy6aefEkIIeeONN8jPP/9MtFot2bNnD4mMjCTx8fHkP//5z6z91Gq15J133iGvvvoqeemll8idd95JWltb9b/fsGEDeeyxx66535IlS8i+ffvIv//9b/LUU0+RoaEhMjg4SH71q1+R/fv3k/3795N7772XvPrqq6S1tZWo1Wpy2223kX//+98kKyuLEELGvI0QQl5++WXy1ltvkU8++YS8//77+rrKqKgoolKprvElJiaGDA8PE0II6ejoIHfeeSd5/fXXx6zH3LRp0zV1oeXl5eTuu+8eUcO4bds28t5775FLly5NycZYPk/0fEz0eEwRHiFsSuBc5/7774ezszNeffVVrl0ZE1P3bzSff/45fvnLX+r/3dbWhs2bN6OgoIBDrxg0mbNbacYVTp8+jbKyMvz8889cuzImpu7faBobG0ckugAgMzNz1vWTjLkFWzHO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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAUYAAAF2CAYAAAASrTFdAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA7EAAAOxAGVKw4bAAB2XElEQVR4nO2dd1yb1/X/PxICxBQbMYUZNsNgG8wwm9hxnMQzSeOkaVbdJG52f2m/aZI2aZrmm9XGaaYzmtUMN44TjzjD8cAMGzAYjG32ElNsIRBIQtL9/eGvVBtjpnQfAff9eullrPGcI+l5Prr33HPP4RFCCBgMBoNhhM+1AwwGg2FpMGFkMBiMMTBhZDAYjDEwYWQwGIwxMGFkMBiMMTBhZDAYjDEwYWQwGIwxMGFkMBiMMTBhZDAYjDEwYWQwGIwxMGFkMBiMMVisMLa3t2P79u1ITk5GRkYGkpKScPPNN2PPnj3QarVcu2cWHnroITz00EPTek1nZycefvhhJCcnIzMzE8nJyVi9ejXeeOMNDAwMzMrmjz/+iOXLl4PH403LJ4Zl8ve//x0bN26kYuuvf/0rgoKCkJmZecXnzOR8pwaxQJqamoivry955513jPdptVryt7/9jQAgjY2N3Dk3DZ555hmSkZEx5ecrlUqiVCqn/Pzm5mbi7+9P3njjDaLX6433f/PNN8Ta2pr88MMPs7Z57NgxMpPTpLm5mTz22GNEKBSS119//bLHBwcHybJly0hSUhJ59dVXp338i/nuu+/IZ599Rl577TWybdu2aX2GpmC873m63z0N1Go1GRgYoGZvss9guuc7TQRcC/N4PPjgg1i5ciW2b99uvM/KygpPPfUUvv76aw49My/29vbTev7999+P2NhYPPjgg5fcv2XLFtxxxx2wtrY2uc2pEhAQgBtvvBEdHR2oqam57PEffvgBKpUKr7/+OtLT02dsZ2BgABs3bkR1dTVCQ0OxceNG/P3vf8fTTz89G/fnJTY2NrCxseHaDSPmOvdMgcVNpfv6+vD9999fccifm5uLwMBAABemkbfeeitSU1ORkpKC++67DwqFAgDwr3/9C+Hh4QgKCsIbb7yBq6++GsHBwfj000+Nx5LJZLj11luRkpKCjIwMXHfddTh58qTx8c7OTtxyyy1ITU1FamoqHn74YYyMjEzp+O+88w4+/vhjlJWVITMzE6tXrx73dWvXroVQKMSbb7552dRjIv8m+5w++OADrF69+or2Pv7443Ft1tbWIjMzE/Hx8di4cSPKysqm8e1dSklJCW655RbU1dVdcv/p06chFovR3NyMxMTEGR8fAEQiEU6dOoWQkBAAACEEer1+Sq81TPfS0tLw6KOPIisrC5GRkXjxxRcved7rr7+Oq666CmvWrMGqVavwzjvvGB8b73u+0nc/nfPp4u9pKufyZHzxxRdYvnw5goKCpvS+gInPv4muvbH86U9/wlVXXXXJZzveuWdRcD1kHUtBQQEBQH766acJn6fT6UhCQgJ59NFHCSGE6PV6cuutt5JNmzYZn/PRRx8RGxsb47G++OIL4uzsTLRaLdHpdGTlypXG1xNCyPPPP08eeeSRS47/8MMPE0IuTOU3btxIHnrooSkdn5ArTyUMr9uzZw8h5MLU9/vvv7/k+ZP5V1hYOKXPaSJ7Y30cHR0lS5YsIX/4wx+M/9+4ceOMptKEEPLqq6+SmpoaEhoaarxPpVKRb775huzatYtcddVVlzy/o6ODPP744xPeDh48eEV7/f39JCQkhDQ3N0/Zx2eeeYbw+Xxy8uRJQgghLS0txMXFhXzxxRfG5+zYsYOMjIwY/Q8NDSX5+fmXHGOyqfR0zqfxvqfJzrU33niDBAYGEpVKdcX3euzYMSKRSKb0viY6/6Zy7Rk+A1tb2yt+tpYYbjBgccJYVFREAJBDhw5N+DyDgNbU1Bjvy8nJIQCIVColhFw4mUQikfHxyspKAoC0t7eP+3q5XE5Onz59xeN//fXXxM7OzhjPm+j4hEwsjM7Ozpfdf/HzJ/PvSsL4xhtvkIyMDLJkyRLy2GOPTWhvrM28vDwCgNTW1hof/+qrr2YkjBqNhrz11ltEo9EQoVBovIC/+uorotFoyH333Ueee+65aR/3Suzfv5/8+te/vkTQpsIzzzxDoqKiLrnvtttuI1dffbXx/0eOHCHr1q0jKSkpJCMjg4hEIvLyyy9fcozJhHGq59OVvqfJzrWvv/6arF271vg5j8dYYZzofU10/k3l2jN8BhN9tpYsjBYXY1y8eDGsrKzQ3Nw84fOkUikAwNvb23if4W+pVGqcbru4uBgfFwqFAAC1Wj3u60UiEVasWHHJ8bdt2wY+/0LEQaVSwcvLC93d3fDy8prw+JPh6uo67fd3sX9hYWHg8/nG5xl48MEH8eCDDyIzMxM9PT1TtgcAra2tAGB8bwDg4eEx6evGo7CwEAkJCbC2toZYLIZUKoVKpcLixYthbW2NY8eO4cMPP5zRscdjw4YN2LBhA2655RbU1NTgmWeemfJrx75HLy8vnD59GgDQ2NiI6667Dm+//TZ+/etfAwAyMzOhVCqn5d9Uz6eJvqeJzrUbb7wRN95445T9mex9TXT+ffXVV5c9Nt61B0z82VoyFieMIpEImzdvxv79+7Ft27ZLHtNoNNi4cSNee+01SCQSABdiHc7Ozsa/ARgfm4jxXj84OIimpiZER0cbYzFffvkl/Pz8jK/r6uq6RDjMxWT+ubq6Yv369dizZw/uuecek9gMCAgAcOE9GmxeLK7TobS0FA888AAAIDQ0FNXV1RgYGMAtt9yC9vZ2tLW1ISEh4ZLXyGQyvPbaaxMeNz09Hdddd53x/7t378bjjz+Ouro68Pl8ZGZm4ve///20hLG3t/eS/3d1dcHf3x/AhTipWq3Gli1bjI9rNJopH9sA1+fTWCZ7XxOdf9O59ib6bC0Zi1t8AYA33ngDZ86cwbvvvmu8T61W49FHH4W1tTXCw8MRHx+PlStX4u233wZwIej+zjvvYMOGDZf8Yl2Jsa8HgBdeeAHffvstAGDlypVITEy8JCB99OhRbN68ecrvQyQSYXBwEADw6KOPoqCgYMqvncw/4EIA+9y5c3j55ZcvWXA4d+4cWlpajCOTqZKYmIiIiAi89957AACdTodPPvlkWscwMDo6arQfEhKCd99917hQdOzYMaSkpFy2ai4Wi/Hiiy9OeLtYFIEL33tGRobRVlNTE+Li4qbla21trfG7aWtrw8GDB3H33XcDAJYsWQIej4ecnBzj8c+cOXPJ68f7nsfep9VqZ30+TcSePXtwzTXXTHnhabL3NdH5N51rr6Ki4oqfrUXD8VT+ishkMnLPPfeQhIQEkpGRQRITE8nTTz99SXC5o6ODbN26laSkpJBVq1aR3/zmN0QulxNCCNm1axdZsmQJsbW1JVu3biUdHR0kMTGRACCJiYmktrbW+Prk5GSSkpJCfvvb3xKNRnOJD7feeitZtWoVycrKIhs3bjQG9qdy/KamJhIdHU1SUlLItddeS1Qq1SWvy8jIIKWlpYSQC7FBiURCRCIRuemmmy55f1fyz+DjAw88QOLj40l6ejpZtmwZSU5OJm+++SYZHBy8or0r2aytrSUZGRkkLi6OrF27ljz33HMEAMnIyCCtra2EEEJ27txJli1bNu73VlJSQu644w4SHBxMPv30U0IIIa+//jr5+eefiV6vJ8899xyJjo4m8fHx5PPPP5/h2fFf9Ho92blzJ/nHP/5BXnzxRXLrrbeStrY24+MT+UrIf+NcTzzxBMnKyiIRERHkhRdeuOQ5r732GgkMDCRr1qwh99xzD4mJiSESiYS88cYbhBAy7vc83n1TPZ/Gfk9TOddmsvgy2fua6Pyb6NojhJBnn32WSCQSkpqaSh566CGSkZFxyWc73rlnSfAIYV0CGdPj7rvvhpubG/7xj39w7cqkTObrX/7yF2RnZyM7O5uuYxxw7Ngx/OY3v0F9fT3Xrlg8FhdjZFg2R48eRUVFBX7++WeuXZmUueSrucjNzUVubi6efPJJVFRUIDw8nGuX5gRsxMiYFiMjI+Dz+bC1teXalUmZzNe//vWv+PDDDyGXy7Fu3Trs2rWLsofmp76+Hrfccgvs7e1BCMF7773HxHEKMGFkMBiMMVjkqjSDwWBwCRNGBoPBGAMTRgaDwRgDE0YGg8EYAxNGBoPBGAMTRgaDwRgDE0YGg8EYAxNGBoPBGAMTRgaDwRgDE0YGg8EYAxNGBoPBGAMTRgaDwRgDE0YGg8EYAxNGBoPBGAMTRgaDwRgDE0YGg8EYAxNGBoPBGAMTRgaDwRgDE0YGg8EYAxNGBoPBGAMTRgaDwRgDE0YGg8EYAxNGBoPBGIOAawcYDADIz89HTk4OdDod0tLSkJGRwbVLjAUME0aGRRAdHQ2tVgulUslEkcE5bCrNsAiam5thY2ODsrIyrl1hMMAjhBCunWAwFAoFnJ2d0d/fD1dXV67dYSxwmDAyGAzGGNhUmsFgMMbAFl8YFgGPx7vsPjaZYXAFGzEyLIK33noLhBD8/ve/ByGEiSKDU5gwMiyC+++/H11dXZBIJFy7wmAwYWRYBoQQHD16FHfeeSfXrjAYTBgZlsFrr72GEydOYOfOnVy7wmCwdB0Gg8EYCxsxMhgMxhiYMDIYDMYYmDAyGAzGGJgwMhgMxhiYMDIYDMYYmDAyGAzGGJgwMhgMxhiYMDIYDMYYmDAyGAzGGJgwMhgMxhiYMDIYDMYYmDAyGAzGGFgFb4ZZIIRgYGAAHR0d6OjoQHt7u/HfgYEBaLVa4210dBSjo6PQ6XQQCAQQCASwtrY2/i0QCODi4gI/Pz/4+PjAx8cHvr6+8PHxgZOT07jVvxmM2cCq6zBmxMjICMrLy1FVVWUUvNbWVqMQdnZ2QqVSwdXVFZ6envDw8ICnpye8vb0hEokuEz6BQIDOzk54e3uPK5pyuRxdXV3o7u5Gd3c3enp6IJfLYWdnB29vb/j6+hpvBgGNiIhAdHQ0bG1tuf64GHMMJoyMSRkeHsaZM2dQUlKC4uJiFBcXo7q6Gp6enggJCYGXl5dRnPz8/BAQEACJRIKAgAAIhcIp2dDpdPjuu++wfv16WFlZTek1KpUKUqkUUqkULS0taGtrM4pyV1cX6urq0NfXh4iICKxcuRLx8fGIi4tDdHT0lP1iLEyYMDIuQalUGkXw1KlTKC4uRk1NDcRiMSIiIhATE4PExESkpKTAz8/PZHZnIoxToaWlBbm5uSgqKkJ5eTkqKyvR09OD8PDwS8QyJiYGdnZ2JrPLmNswYVzg6HQ6nDx5Evv27cPBgwdRU1NjnIYuW7YMCQkJSEtLg1gsNrsf5hDG8WhtbUV+fj4KCwtRXl6OiooKdHd3IyIiAuvXr8emTZsQHx8PPp+tTS5UmDAuQAYHB3Ho0CHs3bsXBw8ehK2tLTIzM7FhwwasXr0a3t7e1H2iKYzj0dbWhqNHj+LAgQPIzs4GAKxfvx6bN2/GmjVrYG9vT90nBncwYVwgNDc348CBA9i7dy9ycnIQFhaGNWvW4KabbkJycjLnoyOuhXGsL8ePH8eePXtw5MgRSKVSZGVlYcuWLVi/fj18fHw49Y9hfpgwzlMIISgpKcH+/fuxd+9e1NTUIDExEddccw22bt2KkJAQrl28BEsSxrFUV1fjP//5D3766ScUFxdj6dKl2LRpEzZt2oSYmBiWLjQPYcI4z+js7MTHH3+M9957D3K5HBkZGdiwYQNuuOEGiEQirt27IpYsjBfT19eHPXv24MCBA8jNzYW3tzfuu+8+3HHHHXB3d+faPYaJYMI4D9Dr9fj555/x7rvv4ocffkBSUhK2bduGW265BQLBzHP4C6T9OFbXi6xQdyRJXE3o8eXMFWG8GLVajc8++wwfffQRSkpKsHnzZtx3333IyMhgo8g5Dlt2m8P09vbihRdeQFBQELZt24agoCCcO3cOx44dw69+9atZi2LKm/n480/VSHkzHwXSfhN6Pj+wtbXFtm3bkJeXh9LSUri5ueGWW25BaGgoXn31VQwMDHDtImOGMGGcg5w7dw7btm1DYGAgfvzxR/zjH/+AVCrFq6++arLY4bG6XvB4POj0BDweD9n1vSY57nwlPDwcb731FlpbW/GXv/wFu3fvhr+/Px588EHU1NRw7R5jmjBhnCPo9Xrs378fmZmZWLVqFTQaDQoKCnD8+HH84he/MPn0MyvUHYQQWPF5IIQgM4TFz6aCQCDA7bffjpMnT+LYsWPo6urC8uXLsW7dOvz0009gkau5AYsxWjiEEPzwww94/PHHIZfLcffdd+ORRx6hEugvkPYju74XmSEsxjgbOjs78eqrr+LTTz+Fv78/Xn75ZWRlZXHtFmMiCMNiOXHiBElNTSVeXl7kpZdeImq1mmuXzIZWqyV79+4lWq2Wa1fMxsjICHnmmWeIm5sbufrqq8np06e5dolxBdhU2gKpqKjAxo0bsW7dOiQnJ6O+vh7/8z//AxsbG65dY8wCoVCIv/zlL6itrUV4eDjS09OxdetW1NfXc+0aYwxMGC2IlpYW3HXXXYiPj4e3tzfq6urw0ksvwdHRkWvXGCbEzc0Nr7/+OiorK2FjY4OYmBjcf//9kMlkXLvG+D+YMFoAvb29eOyxxxAeHg6lUony8nK8//778PT05No1hhnx9/fHv//9bxQXF6OlpQVhYWH405/+BIVCwbVrCx4mjByiVCrx/PPPIzg4GGfPnkVubi52795tcdv1GOYlIiICBw4cwI8//oicnBwEBQXh1VdfhUql4tq1Bcu8Fcbi4mIcPXoUO3bs4NqVcTl06BCWLFmCffv24ZtvvsGhQ4cQGxvLtVsMDklJSUFOTg4+/fRTfPLJJ4iKikJubi7Xbi1I5q0wVlVVITk5GQ0NDVy7cgkKhQLbtm3D1q1b8cc//hEFBQVYvXo1124xLIj169ejtLQU9957L66//no88sgjGB4e5totAEB+fj5eeOEF/O1vf8Px48e5dsdszOs8xu+++w5CoRB2dnbIycmBTqdDWloaMjIyOPHn559/xt13343Fixfjww8/RFBQECd+WCLzOY9xNlRXV+POO+9ET08PPvnkE6SkpHDqj0KhQGlpKZRKJa677jpOfTEn83bE+Pzzz2P9+vX45z//iejoaCQnJyM2NpYTUVQoFLjnnnvwi1/8Ao8//jgOHz7MRJExJZYsWYITJ05g27ZtuPbaa/Hoo49yOnpsbm6GjY0NysrKOPOBBvNWGOPi4pCfn4/Y2FhOv8zDhw8jKioKdXV1KCsrw0MPPcR5UVjG3ILP5+OJJ57AqVOncPLkScTExODEiROc+BIYGIhVq1bht7/9LSf2qcF1hjkNBgYGCCGE9PX1UbOpUCjIvffeS0QiEfnnP/9JdDodNdtzkYWw88UU6HQ68vzzzxMnJyfy6KOPkuHhYa5dmpcsiKGLs7MzAMDV1bz7fQ0cOXIEUVFRqK6uRmlpKR5++GE2SmSYBD6fjyeffBJFRUU4ceIEYmJicPLkSa7dmnfMvGCfhTNZoVBihjUnnU6Hxx9/HB988AGeffZZNm0eg1arhVqthkqlgkajgV6vByEEhBBotVoAF3b/WFlZgcfjgcfjgc/nw8bGBkKhEEKhkC3M/B/h4eE4efIkXnzxRVxzzTV47LHH8Oc//9ns59t415U5riWumder0ga+/vprDA0N4a677jKbDblcjq1bt6KlpQX79u1DWFiY2WxZIjqdDgqFAkqlEiqVCiqVyiiChptWq4VAIIBQKISNjQ34fD74fL7xYuvs7DR2KDSIpl6vh0ajMb7e2toatra2RqE03GxtbeHo6AhnZ+cF92N05swZ3HDDDVi+fDk+/fRTODg4mNUejeuJa+btiNFAaWmp2YPV1dXVWL9+PUJCQlBQUGCcus9XdDodBgYGMDAwALlcjoGBASgUCgiFQjg6OhrFysXF5TLxulJVcUO6Tnx8/BVHhVqtdlzBlcvlUKlUGBoagkajgZOTE1xcXIw3JyeneT3SXLZsGYqKirBlyxasWrUKBw4cgEQiMYstGteTJTDvhbG7uxv19fVmq2Dy008/YevWrdi2bRteeeWVeTdaIYRAoVCgt7fXKISDg4NG4ROJRPD19YWLiwtsbW3N6otAIICjo+OERTUMQjkwMIDOzk5UV1dDrVbD2dnZ6K+7uzucnJzmVV8Wd3d3HD16FA888ABWrlyJb7/9FqmpqSa3Y+7ryVKY98K4du1afPnllxgaGjLpcQkhePXVV/Hss8/i9ddfn1fTCp1Oh56eHshkMnR2dkKn08Hd3R0uLi7w8/ODSCQyuwjOFKFQCLFYDLFYbLxPpVIZRb2rqwuVlZWwtrY2Ps/d3X1e/KAJBAK8++67iI6OxrXXXotXX30V99xzj0ltmOt6sjQWRIzR1KhUKtx77704evQo9uzZg8TERK5dmjVqtRqdnZ2QyWTo6uqCnZ2dUTjc3NzMPrqiufNFr9ejr68PMpkMMpkMarUa3t7eEIvF8PLymhd1L48cOYJbb70VW7duxY4dO2bVGG0hwoRxmnR0dGDz5s3GHiw+Pj5cuzRjlEol2tvbIZPJIJfL4ebmZhQI2jUgudoSSAjB0NCQUSQNn4NYLIavry/s7Oyo+WJqGhsbsXHjRnh6emL37t2s7/U0YMI4DYqLi7Fx40ZkZWXhww8/tNjp5ETodDrIZDI0NTWhv7/fKITe3t6cjpQsZa/02JGzh4cHJBIJvL29LXK6PVnvb6VSiVtuuQXnz5/HgQMHEBUVxYGXcw8mjFPkm2++wZ133oknnngCTz75JNfuTBulUommpiY0NzdDKBRCIpEgICAA1tbWXLsGwHKE8WLUajVaWloglUoxOjqKwMBABAUFwd7enmvXAPy39zePd6GTY/6DKeOKo16vx1NPPYW3334bu3fvxtq1aznwdm7BAg9T4IsvvsD27dvxySef4IYbbuDanSlDCEF3dzcaGhrQ09MDPz8/JCUlwcXFZV6tyJoLW1tbhIaGIiQkBH19fWhqasLRo0fh5eWFkJAQKrHXibi497cV/0Lv7/GEkc/n44UXXsCSJUtw44034osvvsCGDRs48HjuwIRxEj788EM8+uij+Oqrr7Bu3Tqu3ZkSOp0OLS0taGhowOjoKIKDgxEbGzsvFhW4gMfjwd3dHe7u7lCpVGhqasKpU6cgFAoRHBwMf39/TqbZWaHuID9Ovff3XXfdBXt7e/zyl7/Exx9/jBtvvJGSp3MPJowT8M477+CJJ57At99+OyeKyRJC0NraiqqqKtjY2GDJkiXw8fGxyNjYXEUoFCI8PBxhYWFob29HXV0damtrERERAR8fH6ojyCSJK/IfTJlW7++bb74Ztra2uP3226FWq/HLX/6SgqdzDyaMV2DHjh147rnncODAAaSlpXHtzoQQQtDZ2YnKykro9XpERUVRv0gXGlZWVggICIC/vz9aW1tx/vx51NbWIjIykmoTsySJ65QE8WI2bdqEr776CjfffDOGh4fxm9/8xkzezV2YMI7Da6+9hueeew7//ve/LV4U+/r6UFFRAaVSifDwcAQEBLARIkV4PB4CAgLg5+eHpqYmlJSUwNnZGZGRkXBxceHavSuybt06vPfee7jvvvvA4/Gwbds2rl2yKJgwjuGdd97BX//6V3z11VfQaDRoaWlBQEAA125dhkKhQGVlJXp7exEWFobg4GCLWc2dS0yW7jJV+Hw+goODERAQgIaGBuTn58Pb2xvh4eEW2Re8sbERIpEIn332GW677TbY2dmxafVFMGG8iA8//BBPPPGEcfrc19eHgoICALAYcVSpVKisrERbW5txUcVSUm7mGpeku/x45XSX6WBtbY0lS5YgKCgINTU1yM7ORkBAACIiIixm8auxsdHYLE4kEmHXrl245ZZbYGtryxZk/g825/o/vvjiCzz66KPYs2ePcfrs5uaGpKQknD17Fi0tLZz6RwhBW1sbjh07BkIIVq9ejcjISCaKs+DidBce70K6i6mwtbVFdHQ0rrrqKmg0Ghw9ehQymcxkx58pY0URAK677jp88sknuOuuu/Ddd99x7KFlwIQRwN69e7F9+3bs2rXrstVnSxBHtVqNU6dO4dy5c4iNjUVsbOyc3qpmKWSFuoOQqae7zAR7e3vEx8cjJiYGpaWlOH36NDQajcntTIXxRNHAli1b8MEHH+DWW2/F4cOHOfHPkljwO19KS0uRnp6ODz/8EL/4xS+u+DzDtDo6OprqtLqtrQ3l5eUQi8VYunTpvB0hcrXzpUDaP610l9mgVqtRXl6Ovr4+LFu27JIKQOZmIlG8mH/961947LHHUFRUhMWLF1Pzz9JY0MLY2dmJuLg43HvvvXj66acnfT5Ncbz4Ilq+fLmxsvV8xRK3BJqL9vZ2nDlzBt7e3oiOjjb7j91URdHA7373Oxw8eBBFRUUWvbJuThasMKrVamRmZiIgIAC7du2acooLDXE0jBJpXTiWwEISRoDeD990RRG4sLf62muvBQB8//33C+L7GMuCjDESQrB9+3ZoNBp8+umn08r7M2fMUavVori4+JJY4kIQxYWIra0t4uPjsXTpUpw+fRplZWXQ6XQmtTETUQQupB7t3r0bzc3NePzxx03q01xhQQrj66+/jkOHDmHfvn0QCoXTfr05xHF4eBi5ubnQarXIysqa91NnxgX8/Pxw1VVXQalUIj8/HyqVyiTHnakoGnB2dsb+/fvx8ccf45NPPjGJT3OJBSeMP//8M/785z/jq6++gr+//4yPY0px7O3tRU5ODry8vJCYmGgx+W4MOtja2mLVqlUQiUQ4fvw45HL5rI43W1E0EBYWhs8//xwPPvigMZ93obCghLG2thY333wzduzYgZSUlFkfzxTiKJVKUVBQgKioKERFRbH9zQsUPp+PZcuWYfHixcjPz0dbW9uMjmMqUTRwzTXX4Omnn8amTZtm7NNcZMHsfBkYGMCGDRtwxx13mHRfqEEcp7tDRq/X49y5c+jo6EBycjJcXc2bKsKYGyxatAhOTk44deoUFAoFwsPDp/xjaWpRNPCHP/wB586dw8aNG5GXl7cgcmgXxKq0TqfDhg0boNVq8eOPP5qlyMJ0Vqs1Gg1OnToFrVaLhISEBXGijYderwchBHq9HlqtFocOHcLatWshEAjA5/PB5/MX7AhaqVSisLAQDg4OU1qEM5coGtBoNMjIyIBEIsGXX34577+XBSGMTzzxBL755hsUFRWZ5aQxMBVxVCgUKCoqgqurK5YvXz5vUyF0Oh0GBwcxPDwMlUp1yU2tVkOlUk1pB4iNjQ2EQqHxZmtra/zbwcEBTk5O87aa0OjoKE6fPg2lUonExEQ4ODiM+zxzi6KBzs5OJCQk4P7775/3q9XzXhhzc3Nx/fXXo7CwEBEREWa3N5E49vf34+TJkwgLC0NoaOi8+dXV6XRQKBTG3s1yuRwKhQK2trZwcHC4orDZ2trCysoKPB4Per0eBw8exPXXXw8+nw9CCHQ63SVCOlZYh4aGMDo6CmdnZ7i4uMDFxQUikQjOzs7zRiwJIaiqqoJUKkVycjKcnZ0veZyWKBooKirCVVddhby8PCxfvtzs9rhiXscYh4eHcdddd+Gpp56iIorAlWOOvb29KCwsRFRUFCQSCRVfzAUhxNiXubu72yiCBnESi8VwcXGZViqU4feZx+MZRc3KymrCFXpCCFQqFeRyOQYGBtDR0YHKykqjWHp6ekIsFsPV1XXO/gjxeDxjZZ78/HysWrXKuBuFtigCQEJCAh544AHccccdKC4unrcZFPN6xPjII4/g1KlTyMvLoz6CuHjkKBQKUVRUhGXLls0qRYhLRkdH0d3dDZlMhs7OTlhZWRkb1Lu6us66lawpd76oVCr09/cb26ACMLaJ9fT0nLPN5xsbG1FZWYlVq1ZBLpdTF0UDWq0WsbGx2LJlC5599lmqtmkxb4XRMIUuKSlBWFgYJz709fXhxIkTIIQgLi4Ovr6+nPgxU0ZHR9HW1oaOjg709PTA2dkZYrEYYrEYzs7OJh2FmWtLICEEcrncKOhDQ0Pw8PCAr68vfH1955xINjc3o7y8HDweD6mpqdRF0cDp06eRnp4+b6fUc+usmCJKpRJ33nkn/vSnP3EmigCMW7x4PJ7Jt3uZC0II+vv7IZVK0dbWBhcXF/j7+2P58uVzcvWcx+PB1dUVrq6uiIiIwPDwMGQyGZqamnD27Fn4+/sjKCiIM4GZLhefR3q9njM/YmNj8dBDD+H2229HSUnJvJtSz8sR48MPP4ySkhLk5uZyFoTv7e1FQUEBVqxYAaFQyEnJsumg0+nQ3t6O+vp6jIyMIDAwEIGBgXBycqJmn3YRCYVCgaamJrS2tsLJyQnBwcEW3VXx4piiXC7H+fPnkZKSwpmoa7VaxMXFYdOmTfjrX//KiQ/mYt4J4/Hjx7FhwwZOp9CG1eeYmBhjTJGreo6TMTo6ioaGBjQ2Nhr7JPv5+VFPI+Kyuo5Wq0Vra6uxD3dISAiCgoIsapo93kKL4b6UlJTLVqtpUVpairS0NOTm5mLFihWc+GAO5pUwKpVKLF26FPfffz/+8Ic/cOLD4OAg8vLyEBkZednqsyWJo06nQ1NTE2pqauDi4oLFixfDzc2Ns9VbSyg7RghBd3c3ampqoFQqsWTJEgQGBnI+gpxo9bm+vh61tbVIT0+Hvb09J/49+eSTOHDgwLyaUs8rYXzwwQdRVlaGnJwcTk5mjUaDnJwcBAYGXrH6MdfiSAhBa2srqqqqYGtri8jISHh4eFD3YyyWIIwGCCHo6upCRUUF9Ho9IiIiOOvTPZWUnPPnz6O7uxupqamcjHK1Wi1WrlyJDRs24LnnnqNu3xzMG2HMzs7Gxo0bOZtC6/V6FBYWwtraGnFxcRNeRFyIoyVd7ONhScJowNCArLKyEjY2NoiMjISnpyc1+1PNUySEoLCwEFZWVli5ciUn36lhSp2Tk4PY2Fjq9k2NZUaZp4larcbdd9+NP//5z5zFFSsqKqBWq7FixYpJT0zaDbaGh4dx8uRJlJWVITg4GFlZWfD19bUYUbRUeDwe/P39sXr1agQEBKCkpASFhYUYGRkxu+3pJG/zeDzExcVBoVCgpqbG7L6Nx4oVK/Doo4/irrvumjMZGBMxL4Tx7bffhrOzMx577DFO7Dc3N6O1tRWJiYlTHu3QEEdCCBobG3Hs2DGIRCKsWbMGEomE85jZXIPP5yM4OBirV6+GUCjEsWPH0NLSAnNNtmayo8Xa2hqJiYmor69He3u7WfyajL/85S9QKpX47LPPOLFvSub8FaJQKPC3v/0Nzz33HCcXfF9fH86ePTujKjnmFMfh4WGcOHEC9fX1WLVqFaKioixmijpXsba2xrJlyxAfH4/KykoUFRWZrOK2gdls83N0dMTKlStRWloKhUJhUr+mgkAgwJ///Gf86U9/MvnnQps5L4yvvPIKIiMjsXHjRuq2R0ZGUFRUhOjoaLi5uc3oGKYWR0IImpqajKPErKysGfvGGB9PT09kZWVBKBTi6NGjJhs9mmLvs5eXF8LDw1FYWAi1Wj1rn6bLHXfcAXd3d7z99tvUbZuSOb340tnZidDQUPz4448mqcg9HbRaLfLy8uDh4YGlS5fO+nimWJBRqVQ4ffo0hoeHERsbO6cE0RIXX6ZCd3c3SktLIRKJsGLFihmnq5iyIAQhBGVlZVAqlUhOTqY+kzp48CBuv/12NDU1cZZfOVvm9IjxueeeQ0ZGBnVRBIDy8nLY2toiKirKJMeb7chRLpcjJycH9vb2bJRIEcPoUSAQICcnB4ODg9M+hqmr5PB4PMTExIAQgvPnz8/6eNPl+uuvR1RUFF555RXqtk3FnBXGhoYGfPTRR3jppZeo25bJZOjq6kJsbKxJV3ZnKo5tbW3Iz89HWFjYvC5+a6lYW1sjNjYWEokEubm56OzsnPJrzVU6zJC609LSgp6eHpMdd6q8/PLLeO2116b1WVgSc1YYn3rqKWzZssVkI7apotFoUFZWhpiYmFmX2hqP6YgjIQSVlZUoLy9HQkICFi1aZHJ/GFODx+MhLCwMsbGxKCkpQW1t7aRxR3PXU7Szs0NUVBRKS0uh1WpNfvyJWLVqFTIzM+fsHuo5GWMsKytDWloaKioqqO8eKSkpASEEK1euNKudyWKOUy17P1eYqzHG8ZhK+wpaRWYJISgoKICDgwNiYmLMZmc8KisrsXLlSpSXlyMkJISq7dkyJ0eMjz/+OO68807qotjR0YHu7m4qJ9hEI8eRkRHk5uYCANLS0ua8KM43nJ2dkZ6eDrVajby8vMtWh2lW3ubxeFi+fDlaW1upT6kjIiJwww034KmnnqJq1xTMOWHMzs7GqVOnqA/RNRoNzpw5g2XLlk1p5bFA2o8XjtShQNo/Y5vjiePw8DDy8vLg6emJhISESbvHMbjBxsYGSUlJcHZ2Rn5+vjGvj4t2BFxOqf/3f/8XBw8eRGlpKVW7s2VOTaUJIUhISMC1115LXRhLSkoAAHFxcZM+t0Daj5Q388Hj8UAIQf6DKUiSzLxvtGFavXjxYjQ0NMDf3x8RERHzakvffJpKXwwhBOXl5ejp6UFAQADq6+s5aUdg2E9tb29PfUr90EMPobq6GocOHaJqdzbMqRFjdnY2pFIp/vjHP1K1a5hCR0dHT+n5x+p6L1Tt1hPweDxk1/fOyr6bmxuWLVuG8+fPw8XFZd6J4nzGkDojFApRWVmJuLg4TgrL8ng8LFu2jJMp9bPPPouCggKUlZVRtTsb5pQw7tixA7/61a+uWHfOFNPXsUx3Cg0AWaHuIITAin9hxJgZ4j4rH4aHh3H+/HkEBgaip6cHra2tszoegy5NTU0YGBiAv78/zp49y9l2OTs7OyxdupT6lNrNzQ033XQTduzYQc3mbJkzU+mGhgYsXboUtbW18PPzu+xxU09fDZSXl0Oj0Ux7FbpA2o/s+l5khrjPyo+RkRHk5+fD19cXERER6O/vt5hit6Zkvk6lL44pOjs7o7y8HL29vUhJSTFLutdkGFapDTMPWlRVVSE2NhZNTU3w8vKiZnemWE7t9kl44403sG7dunFFEbh0+mrFvzB9na0wKpVKNDc3Iysra9qvTZK4ztq+VqtFQUEBvL29jdPnK/WttnT0ej0GBwcxMjIClUp1yU2n0xkbO504cQJ8Ph8CgQBCoRC2trYQCoUQCoWwt7eHo6PjnKkONN5CS0xMDEpLS1FUVITk5GTqPwI8Hg9RUVHIzc3FokWLptX7ezaEh4dj1apV2LlzJ55++mkqNmfDnBgxDg4Ows/PDz/99BNWrVo17nPMMWI0lGqfamzRlBBCcOrUKej1eiQmJl4WU+S6EvhEEEIwODiIvr4+DAwMQC6XQ6FQwNraGvb29kahMwiftbU1CCEoLi42FlodHR2FSqWCWq02CqhSqYROp4OzszNcXFwgEong5uYGR0dHi4u5TrT6rNfrceLECTg4OGD58uWc+F5SUgJra2uqCzH79+/HPffcg5aWFotvgTAnRoyffvoplixZckVRBC6M0PIfTDHJ9BUABgYGIJPJsGbNmlkdZ6bU1NRgcHAQ6enp4144ljZy1Ol06O3tRUdHBzo7O6HT6eDq6goXFxcsWbIEIpFowrJshuKmYrH4iqMoQghUKhXkcjnkcjna29tx/vx5WFtbG/tdu7u7cz6inCwlh8/nIz4+HsePH0djYyOCg4Op+xgeHo5jx44hJCSEWh7s+vXr4erqiq+++gq/+tWvqNicKRY/YiSEIDIyEo899hh+85vfULN78uRJuLq6Ijw8nJpNA+3t7Thz5gzS0tLg6Og44XO5HDkamkc1NzdDJpPBzs7OKFDTbaw10xijXq9Hb28vOjs7IZPJoFarIRaLIZFI4O7uTn00Np08xYGBAeTl5SE+Pp6TuNvZs2ehVqvNvovrYl5++WV8++23OHnyJDWbM8HihfHkyZPYsGED2traqAWre3p6cOrUKaxZs4Z6ArXhYklISJhyfxFziWOBtB/H6nqRFXrpCFyj0UAqlaKpqQmEEEgkEvj5+U0q4hNhisUXQgiGhobQ2tqK5uZmWFlZYdGiRQgMDKTyPc4kebu9vR1lZWVIT0+f1ec3E9RqNQ4fPoyUlBS4uLhQsSmXy+Hv74+ioiJERkZSsTkTLF4Y77rrLlhbW+P999+nYo8QgpycHPj7+1Pf36lWq3H8+HGEhoZOe3planEcL2a7QmyPmpoaSKVSeHh4YNGiRfDy8jLJqMzUq9J6vR6dnZ1oaGiAXC5HUFAQwsLCzBbbms2OlurqarS2tiI9PZ36D3FVVRX6+/snDFOZml/84hfw9/e36PQdi17eGxgYwO7du/Hggw9Ss9nR0QGNRoOgoCBqNoELglxaWgpPT88ZVckxdSXwi1f57QTAuYpKHD58GBqNBhkZGUhKSoK3t7fFLXoY4PP58PHxQUpKCtLS0qBUKnH48GHU1taaPIdvttv8Fi9eDCcnJ5SXl5vUr6kQEhICuVyO7u5uajZ/+9vf4pNPPuGkwvhUsWhh/OKLLxAZGYlly5ZRsafX61FZWYnw8HDqaRQtLS0YHBxEdHT0jMXGlOKYFeoOHtFjvR/B27E6+NlqkZqairi4ODg5Oc3q2LRxdnZGQkICkpKS0NnZiSNHjkAqlVpMOwJDoYfu7m50dHTM2qfpYG1tjSVLlqCiosJszb3GkpmZCS8vL3z77bdU7M0EixbGnTt34s4776Rmr7293dgykyYjIyM4d+4cVqxYMeuG6aYSx0hXK+y92h63hdogKCIG116VzslWNlPi5uaGlJQULF++HPX19cjPz8fQ0NCMj2fKghA2NjZYtmwZzpw5A41GM6tjTZegoCCo1Wp0dXVRscfn8/HLX/4SO3fupGJvJlhsjLG2thbLly9HV1cXtXSCnJwcSCQSSCQSKvYA823un2nMUa/Xo66uDrW1tQgLC0NoaCiV9BfaO190Oh2qq6vR2NiI8PBwBAcHT2ukbq4qOdMpVmJK6urq0N3dTS3W2N3dDT8/P7S2tlrkThiLHTEeOHAAycnJ1ESxr68PSqWS+mjRMIU29QrdTEaOg4ODyM3NRXt7O9LS0rB48WLOcwLNhZWVFSIjI5GcnAypVIr8/HwMDw9P6bXmLB0WHR3NyZRaIpGgt7d3Rj1rZoKnpyeWL1+OgwcPUrE3XSz2rP/2229x3XXXUbPX0NCAoKAgqrFFU06hx2M64tjZ2Ync3Fx4e3sjPT19znZ3my6urq7IyMiAq6srjh8/jt7eiSshmbue4sVTapqLE9bW1ggICEBDQwM1m9dccw327t1Lzd50sEhh7OvrQ2FhIW6++WYq9kZGRiCTyaivRJeXl8Pf3x8eHh5mszGZOBJCUFdXh5KSEsTGxiI8PHzejhKvhJWVFaKiohAdHY2CggI0NTWN+zxaRWZ9fHzg6emJc+fOmc3GeAQHB6OlpYVajHPr1q04fPgwZ9WGJsIir4AffvgBUVFRVywYYWqam5vh7e094ZY1U9PT04O+vj4qFU6uJI46nQ6lpaVobGxEamoqxGKx2X2xZPz9/ZGcnIzq6mqUl5cbC1sA9CtvL126FDKZDHK53Oy2DDg5OcHNzc0k6V5TYenSpfDx8cHRo0ep2JsOFimMe/fuxdq1a6nYIoRAKpVSHS0SQlBRUYHFixdTS+gdK46Gyj0jIyPIyMhYMFPnyTBMrfv7+3Hq1CnodDpO2hHY2toiJCQElZWVVOwZCAoKMlkq01S46qqrsG/fPiq2poPFCaNGo8FPP/2ErVu3UrHX1dUFHo9n1unsWDo6OqBWq6lP3Q3iWF5ejuzsbAgEAiQlJVl8pRPaCIVCpKSkQKvV4vjx46isrOSkHQEXyddisRgajQb9/aYr9jwRN954I/bv309NiKeKxQljTk4OXF1dERsbS8WeVCqFRCKhtoODyyRy4EKys729PYaHhyesZLPQEQgEEIvFGBoagpOTE/V9zAA3ydd8Ph8BAQGQSqVU7K1ZswYajQanT5+mYm+qWJww7tu3b0aFYWeCSqVCZ2cnAgMDqdgDLqTn8Pl86mlBwIWYoiFnMjk5GefPn6cWT5prNDY2oqamBqmpqeDz+SguLr4k5kiLoKAgaDQaquk7EokEbW1tGB0dNbstKysrpKenW9x02qKEkRCCffv24YYbbqBiTyaTwd3dnVoVY51Oh6qqKkRGRlLfY2zoVkcIwcqVK+Hh4WHSvdXziYtjim5ubkhMTMTIyAgqKiqo+8Ln8xEeHo7Kykpqwuzo6AhnZ2dqO2E2btzIhHEizp07h4GBAaxbt46KPZlMBh8fHyq2gAtNkRwcHDjJ9G9oaEBPTw/i4+ON02dTF56YD4y30CIQCJCYmGgsZ0Ybf39/8Pl8qk3QxGIxZDIZFVs33HADqqurLeoctChh3LdvH9LS0qgsBmi1WnR3d1NLUSGEoKGhAWFhYdRHi11dXaiqqkJiYuJlNS2ZOP6XiVaf7ezskJCQgLNnz6Kvr4+qXzweD2FhYaivr6cWaxSLxejs7KQyShWJREhISMCBAwfMbmuqWJQw/vDDD9RGi93d3XBycqKWuyiTycDj8aiPFpVKJYqLixEbG3vFlBwmjlPLU3Rzc0N0dDSKioowMjJC1T9fX1+o1epJd+aYCicnJ1hbW1P7Ebj66qvxww8/ULE1FSxGGHU6HcrKyqgtvMhkMqoJzQ0NDdMuVDBbCCE4ffo0goKCJg0ZLGRxnE6eYmBgIHx8fFBWVkY1xYTP52PRokWor6+nYo/H48Hb25vadDo9PR3FxcVUbE0FixHGmpoaCAQCKjtBCCHo7OykJowKhQJyuZx6T5b6+npotdop961ZiOI4k+TtqKgoDA0NUY83BgUFobu7m9po1RBnpPEDEB8fj56eHmpCPBkWI4ynT5+mtk+3v78fPB6PWsJuc3Mz/Pz8qJatHxwcRHV1NVasWDGtz3QhieNMd7QIBAKsWLEC58+fpzqltrW1hbe3NzVB9vDwgFqtnlXNyqlib2+PsLAwY9k1rrEYYSwuLsbSpUup2DJMo2lMa3U6HVpaWqhvOSwtLUVISMiMmhwtBHGc7TY/Dw8PBAQEUJ9SSyQSalv2+Hw+vLy8qI3iIiMjLWY6bTHCeOrUKcTHx1OxRTO+aGgrSqsLG3BhhKrVarF48eIZH8Pc4kgIwfDwMDo6OtDU1GQsd9XY2IimpibIZDKzjcZMtfc5IiICQ0NDVJOvPT09wePxqG0TpJm2ExsbazHCaPoigDNAr9fjzJkzeOedd8xua3h4GMPDw9T2Rre3t1OrEgT8N4l8+fLlsw5LGMSxoKAAAGYVI9Xr9ejp6UFPTw8GBgYgl8uh0+ng7OwMOzs74+hdLpdDr9djZGQECoUC1tbWcHFxgUgkgoeHBzw8PGY10jdlQQhDTLyyshJisZhKGIjH48HPzw8dHR1UMhy8vb1RVlYGjUZj9jS6pKQkvPnmm2a1MVUsQhhra2vB4/EQFRVldlt9fX1wdXWlskdYr9ejq6tryosfpqChocGkSeSzFcfh4WE0NjaipaUFAoEAXl5e8PPzQ1RUFBwdHY1iotPp0NbWhhUrVhi/G71ej8HBQcjlcsjlcpSWlhp9CAoKmnaqlTmq5Pj5+aG2thYtLS3UWmKIxWKcOnUKMTExZg8H2djYwMnJCf39/fD29jarraSkJHR3d6Ozs9PstibDIoSxpKQEERERVH5x5XI5tWltT08PbG1tqXXVGx0dRW1tLZKSkkx6wcxEHBUKBWpqaiCTyeDr64v4+Hi4ublNyy8+nw+RSASRSASJRAJCCHp6eiCVSnHkyBH4+vpiyZIlU2p/Ya7SYTweD5GRkSgrK4O/vz+VH1xXV1cQQjAwMEDlXBaJRJDL5WYXK3t7e4SGhqKkpIRq9f7xsIgYY3FxMZXRInChVzWt1WiaKUHAhZG3u7s73NzcTH7sqcYc9Xo9ampqkJOTAzs7O6xZswaxsbFwd3eftVjzeDx4enpi5cqVuOqqq2BtbY3s7OxJd4SYu56il5cXHBwcqLUFoJ1j6OLigoGBASq2IiMjLaLSjkUII62FF0IItREjIYTqIo9Wq0VTUxOWLFliNhuTiaNCoTA200pPT0dUVJTZCnTY29sjOjr6kmZWSqXysufRKDLL4/GwZMkSNDQ0UCv0QHNRxMXFhVol8RUrVqCoqIiKrYngXBgNCy/Jyclmt2W4cGh0HhwcHMTo6KhZRm/j0dLSAmdnZ7OL/njiSAhBfX09cnNzIRaLqTbTMlTcdnNzQ3Z29iX9WmhW3vbw8ICNjQ3a29vNaseAp6cnBgcHqeRROjs7Q6VSUWnOtWrVKovIZeQ8xmiYBtHIYZTL5RCJRFTyF2UyGby9vanETQkhaGxsNOto8WIujjkSQqBQKNDe3o7U1FTqVa6B/7ZCFYvFKCoqglqthrW1Naqrq6lV3ubxeFi0aBGampqo1NoUCATw9PSETCbDokWLzGrLysoKTk5OGBgYMPtKeFJSErq6utDV1cVpv2nOR4w1NTXU2pbSClYDF4pU0Ppi+/v7odFoqJZQM9QpPHPmDNra2pCWlsaJKI71KTU1FfX19Th//jxWrVpF1Sd/f38MDAxQ683s5eVFLZ+R1nTawcHBuNLPJZwLY0dHBzw9PanYohlfpCnCzc3NCAgIoNr21BBDtbW1hVarRU9PDzXbE2EQCmtra2qFVg0IBAL4+flR27JHc1HEsDJNAw8PD6pJ8+NhEcJIY2RlWHihMYIYHh6GXq+n0ifEIFC+vr5mt3UxjY2NxpHiqlWrLGL7oCGmmJKSgtTUVDQ0NFD3ydfXl9pF7ezsjJGRESp9oGkuwHh5eTFhbGtro7JyOzw8DABUxMowMqURyzQUxKC55XBoaAiVlZVISEiAnZ2dReytHrvQ4ujoiJUrV+Ls2bNUCz0YCi/QmE4LBAI4OjpSESyRSASVSkVFhL28vNDW1mZ2OxNhEcJIY7SjVCrh4OBARaxojUwBugUxgP8WqAgODr5EjLkUxyutPnt4eMDf3x9nzpyh2mWPZuEFWtNpKysr2NnZjZsSZWrEYjG11f0rwbkwdnR0UNlLrFKpqDW9ohlfNKx+06KhoQGjo6PjFqjgQhwnS8mJjIzE4OAgVbE2tAWgAc0prlAohEqlMrsdX19fJozt7e1U2pfSEkaascyRkREMDw9TW7xSKpWoqqpCbGzsFbMIaIrjVPIUDbUTz507R+WiBi4UXujr66PSfpSmMNra2lLJZQwICFjYMUa9Xo/u7m4qwqhWqy9rBGUORkZGoNfrqeyP7u/vh0gkopLqBFzYchgQEDDpaJiGOE4nedvDwwPe3t7U2gLY2NjAwcGBimAZFmBoiDCtEWNgYCDnlbw5Fcbe3l4QQqgkxNIaMQ4PD8Pe3n7exTJVKhVaW1sREhIypeebUxxnsqMlNDQUTU1NVAQEoDeSEwgEsLW1NS4umhNawiiRSNDX10dloedKcCqMHR0dcHNzozLioSWMNGOZNCsFSaVSY7GEqWIOcZzpNj+RSAQXFxdqsUaaOYa0BIuWHTc3NwiFQk5HjZwKY3t7O7X42HwTRppJ5ISQGdcbNKU4znbvs0QioSqM821RhJYdPp8PT09PTuOMnI8YaQgjIQRqtZqaMNKIZapUKmi1Wip5mf39/dBqtTNOxDeFOJqiIISPjw+Ghoao5BiKRCIolcp5FfujtfgCcL/7ZUEIo1arhU6noyJYtAR4ZGQEdnZ2VLYB9vX1GXuNzJTZiKOpquRYWVnB3d2dShN5gUAAGxsbKsnltATLIMA0ckIXtDAODQ1RGfGoVCrY2NhQEZH5NmUHTBfLnIk4mrp0GM09v0KhkKpgmRsbGxvweDwq78nR0ZFK29Yrwakwjo6OQiAwf+UzGo18DMxXYTTV6vd0xNEc9RTZosjM4fF4sLGxobJaLBAIqGUQjAenwqjVaqkIIyGEWuUZWvmStIRxdHQUw8PDJk0Lmoo4mqvIrEEYaVTano+xPx6PR2UqbWVlBa1Wa3Y7V2JBjBgJIdT2Euv1eioiTEuAFQoF7O3tYW1tbdLjTiSO5qy8LRQKYW1tTWWaRkuw+Hw+tZYKfD6fijAKBIKFK4xardbkF9x46PV6qkUWaAgjrdG2OReTLhbH1tZWAEBTU9OsRbFA2o8XjtShQNp/2WM8Hg+2trbUpoM6nW5Cf0yBKUZxU/WRx+OZTIQnsmkqYVQoFPjLX/6CH374YVp966d1ZbW1teGf//wnent7AVw4sR9++OEZN2IfHR2FlZUVdDrdjF4/VXQ6HXg8ntntABeEUa/Xm92WXq8HIcTsdrRaLfh8vtnsiEQixMfHGxsgVVVVYdWqVXB0dJyRzaLmfly18yR4PB7+eojg6PZVSAh0veQ5hBBoNBoq31GHXIktX+RN6M9sIYRgdHR0xu9nKp+ZAZ1OZ7zNhslsKhQKkwjwxx9/jLi4OFx77bW46aabcNttt02pHxGPTOOn5uzZswgODkZeXh56enpw2223zcrpu+66C1ZWVti4ceOsjsNgMOYXN954I3Q63axHwg899BDuuusuxMXF4YEHHsBdd901pY6k0xoxRkdH46OPPsLmzZuxc+fOGTtrNC4QwMXFBevXr5/1sSais7MTdXV1SElJMasdADh48CDWrFlj9vhfcXExPD09Z7QbZTq0t7ejubkZSUlJZrNhmD4bwgNLly6d8f75i0cihIw/+snNzUV4eLjZc2jr6urQ1NWPzd91T+jPbBkaGsKJEyewdu3aGb1+Kp+ZgezsbMTExMy6++VkNu+8806TdNjU6/XGLcc6nW7K24+nHaTKz8/H3XffjdLS0um+9DKsra2n5exMEQgEIIRQ2ZPN4/HA4/HMbstwfBqf3cUnl6lpbGxETU0NVq1ahdzcXCQkJODUqVPg8/kzCtGsWuSBY/enIru+F5kh7kiSXH6B6/V6CAQCKueD2NluUn9my2zPual8ZgYIISb57CazqdPpTJJiFxUVhY6ODixfvhwtLS0IDg6e0uumLYzPPvssAODvf//7dF96uXFKuUq0Ugxo2qK1amdnZ2e2yi0Xrz4bEv0vbs0KYEbimCRxveLFTQjB8PAw7OzsZu74FDGMgCfyxxSYYsFvqj6aciFzIps6nc4ki4u/+tWv8Nprr0Gv1yMzM3PKGxWmbdlQbdsUNRStra2plEsy5UraZNDKv6KVI+fk5AS1Wm3yvMmxKTkXB/NNIY5XYmhoCHw+H/b29iY75pVQqVRUinzodDpqebq0si5Mlcrn7OyMp59+GgBw/fXXT/l1nKbr0Br10Mzzmm/bwKysrODs7GzS3SJTyVM0Vz1Hwy4eGulb83EXFK3UN1ONGGcKp8JobW1NRRhp7gyYb9vAANOW0JpO8rY5xJFmP575Jox6vR4ajYbKxgJamz+uBKfC6Orqiv5+8yS9XoyhKfx8EmGaYm8qYZzJjhZTiyPN4r40S9DREEa1Wg0+n09lU8bAwIBJVqVnCqfC6OPjg56eHrPbEQgEEAgE82qK6+DggJGRESpJ6x4eHuju7p7VD8tstvmZShzVajX6+/vh7u4+42NMx5ZOp6OyyEN7fz6NqXRPTw98fHzMbudKLAhhBObfFNfGxgZCoZBKpRhHR0eIRKIZt7Q0xd5nU4hja2srPD09qYyu5HI5nJ2d51XbDloCDDBhRHd3NxVb800YAbrl8wMDAyGVSqf9OlMWhJiNOBJCIJVKqXSkBOg3KptPsUydTrewhdHX1xf9/f3zSrBoCyOt2oJ+fn4YGhqaVvVrc1TJmak4dnV1QavVQiwWm8SPyaAdy5xPwtjR0QG9Xk+tH9R4cCqMzs7OEAqFVBoU0Yz9DQ8PU4n90RwxCgQCLFq0CDU1NVN6vjlLh01XHAkhqK2tRUhICLV8P1qr3xqNBqOjo9TyMmkIo6EjJa1+6ePBqTDyeDx4e3vPaIo2XWiuFguFQigUCrPbcnFxwdDQELX+uyEhIRgYGEBbW9uEzzOnKBqYjjhKpVKoVCqz7ys3oFQqodFoplTFZbbI5XI4OTlRERFaMcbW1lZqI/srwakwAhem083NzWa3Q3OKS6uviI2NDVxdXdHZ2Wl2W8CFvNPly5ejvLz8ij8yNETRwFTEcXh4GOfPn8eKFSuo5cXJZDJ4enpSEav5GMtsbm7mNL4IWIgwTjYCMQXzNfYnFoupNib39vaGWCzGmTNnLtsTTlMUDUwkjoQQlJWVQSKRUEnRMSCTyVgscxZ0dHQYtx5zhUUII402iYauY/Mt9icWi9HV1UXlfRlYunQp+vv7L/lB40IUDVxJHJuamjA8PIyIiAhqvoyOjqKvrw/e3t5U7NGMZarVajg4OJjdlkwmg6+vr9ntTIRFCCONqaBQKIStrS21ZusKhYKKWDk6OkIoFBqrqtPA2toasbGxOHPmDHp6ejgVRQNjxbGzsxMVFRWIi4ujGsTv6uqCSCSiMrLSaDQYGRmhFst0dHSkEo7o7u7mXBi524z4f/j6+lLLZTSM5Mz9C3uxCNP4Nffx8UFrayu8vLzMbsuAp6cnli9fjoKCAvB4PKSmpnImigYM4njixAkQQpCQkABXV/OV+xqP1tZWahc1TbGiuce8u7ubxRhpjRiBCyM5WrE/FxeXaeX8zQaJRIL29nbqfXgvXg3nsgfwxRh84vF41FbrDYyMjKCrq8ukZdImor+/n5pY0Vzk6erqYsIYFRWFxsZGKqk0NGN/Xl5e6OrqomLLwcEBbm5uVFb3DRimz2lpaVixYgUKCwshlUqpFQQeCyEE9fX1KCkpQUJCApKTk01esmwympubIRaLqW2b6+zsnHexzO7ubnR1dVGNC48H58Lo4+MDV1dXY5c4c+Li4mKy7mOTIRaLZ114YTosWrQIjY2NVIRpbEzR19cXSUlJqK2tRUFBAUZGRszuw8UolUrk5+dDKpUiJSUFXl5eZqvneCX0ej0aGxuxaNEis9sCLqwQDwwMUAmfjI6OYnh4mMqIMS8vDyEhIcaK7lzBuTACQGxsLE6ePGl2O4Zm6zSSr+3s7ODk5ERt1CgWi0EIMXtY4koLLe7u7sjMzISjoyOOHTtGZfRICEFDQwOys7Ph5uaGjIyMS0Y1NMWxra0NQqGQWlpQZ2cn3NzcqJQAoxnLLCwsxMqVK81uZzIsQhjj4+NRUlJCxRbtHENa8VMej4ewsDBUVVWZTZAmW30WCASIjo5GQkICampqkJeXh46ODpP7o9fr0dbWhpycHDQ1NSE5ORmRkZHjrj7TEEe9Xo/q6mqEhYVRKckF0M+VpBVfPHPmDBNGAytXrkRFRQUVW7RzDGUyGbW4W2BgILRarVkS5qeTkuPh4YGsrCz4+/vj/PnzOHLkCGpra2c9xR4eHkZ1dTWOHDmCqqoqBAUFISMjY9KVZ3OLo1QqhUAgoLYardPp0N3dTU0Yaa5IG1KsuIbzdB0AiIuLQ01NDZW9mCKRaMqFEExhi8/no7+/n0o1Yj6fj4iICFRWVsLX19dkBRNmkqdoKDoRFBSEzs5ONDc3o7q62ljb0cXFBS4uLlesWajVaqFQKCCXyyGXyzEwMAClUgmxWIxly5bB09NzWqMzczXY0mq1qK6uRmxsLLXRYnd3N+zt7akkWwMXRow0Yqc9PT1obW3FihUrzG5rMixCGH19fSESiXDq1Cmkpqaa1Za7uzsUCgU0Go1J+tZOBI/Hg1gsRkdHB7Uy7b6+vqitrYVUKjXJyTzb5G3DZyAWi6HRaNDf3w+5XI7u7m7U1tZCpVLBxsbGKOJHjhwx9haxs7ODi4sLRCIR/P394erqOquYmjnEsaGhAU5OTlRzSGlOo4eHh6FSqahMpfPz8xEcHAwnJyez25oMixBGHo9nXIAxtzBeXHiBRr6Zn58fSkpKEBkZSWVEwePxEBUVheLiYvj6+s5qBG7qHS02Njbw9va+JMVErVYbS2fl5uZi5cqVsLa2hq2trVl+uEwpjsPDw6itrUVycrKp3JsUnU6H9vZ2pKSkULFnKIhBY+GloKDAIuKLgIXEGAG6CzA0Cy+4u7vDysqK2iIMcGFXire3N8rLy2d8DFrb/GxtbeHk5GS0IRKJ4OTkZNbRvClijoYCFYGBgVR317S3t8PBwYHaYgjN0WlZWRni4+Op2JoMixHGuLg4VFZWUrFFs/ACj8eDRCKhUnPyYqKjo9HX1zejPi2WsPfZ3MxWHKVSKfUCFQa7tOpKjo6Oore3l1oSeVVVlUUsvAAWJow1NTVUtnHRLrwQEBCA7u5uqonPhtqJZ86cmdauooUgigZmKo5c1HgEgMHBQcjlcmoluWgWxOjr60NLS4tFLLwAFiSM/v7+cHBwmJfTaaFQCB8fHzQ2NlKxZ8AQzysrK5tSytBCEkUD0xVHvV6P0tJS6jUegQsLPYGBgVSSugG60+i8vDwEBQVRqRQ0FSxGGHk8HhISEnDkyBEq9mjnGAYHB0MqlVKtmwhcmFIrlUpUV1dP+LyFKIoGpiOO58+fh1arpT6F1mg0aGlpobblUK/Xo7Ozk5owHj9+HImJiVRsTQWLEUYA2LBhA3766ScqtlxdXY25crTsOTg4oLW1lYo9A9bW1khMTERjY+MV440LWRQNTEUcpVIp2trakJCQQL1RU3NzM9zd3amlsvT19cHa2pqavcOHD2PDhg1UbE0FixPGU6dOUdmyx+fz4e3tTbUtQGhoKGpra6kUsbgYBwcHrFy5EqWlpZd9tkwU/8tE4tjb24tz584hISEBdnZ2VP3SarWoq6tDaGgoNZuGaTSNFLOWlhZUVlZi3bp1Zrc1VSxKGP39/REeHo49e/ZQsWdIvqaFj48PBAIB9RVq4EIKT0REBAoLC42LQEwUL2c8cRwaGsKpU6cQExNDLVH/Yurr6+Hk5EStzzIhBDKZjNpq9K5du5CSkkJt2+FUsChhBIBNmzZh//79VGx5e3tDqVRSm07zeDxERkaiurqaWjmyi1m0aBH8/PyQn5+PmpoaJopX4GJxrKurM+7IoFWA9mI0Gg3q6uoQGRlJzWZfXx+0Wi08PDyo2Pvhhx+wefNmKramikUKY05ODhXhEAgE8Pf3pzqC8/LygrOzM+rr66nZNGAQZqFQiMrKSsTFxTFRvAJubm5Yvnw5zp8/DxcXFyxevJgTP2pqauDl5UU1iVwqlSIwMNBke+0nQqlUoqCgwKLii4AFCuOKFSsgFAqprU5LJBK0tLRQXS2OjIxEXV0dlarlY2lqaoJCoYCfnx/Ky8upF5WdKwwNDeHs2bMIDAxET08P1UrgBoaHh9HU1ES9y2F7ezu1JPK9e/ciKCgIwcHBVOxNFYsTRh6Ph40bN+Lrr7+mYs/FxQX29vYz2iEyG5teXl6ora2lZhP4b0wxJSUFcXFx8PHxQU5ODvr7+6n6Yen09PQgNzcXixYtwooVK7Bq1SrqbRIAoLq6GgEBAVSrWbe0tBgzKGiwb98+bNq0iYqt6WBxwghcmE4fPXqUmr2goCDqCyIRERHGvsc0GLvQYig2sWTJEpw4cYJ6GpGl0tTUhMLCQkRHRxunz7TbJACAQqFAW1sb1Sk8IQRSqRRBQUFU7On1emRnZzNhnCpZWVmQyWQ4e/YsFXv+/v5QKBTUKnsDF7YlBgUFTXlXymyYaPU5KCgIiYmJOHv2LCoqKjhrZsU1er0e5eXlqK6uRnJyMvz9/S95nKY4GgpUhIWFUU0N6u3thVqtptahLycnx9jm1tKwSGEUCoW4+uqr8Z///IeKPYFAgMDAQDQ0NFCxZyA8PBzDw8NmHa1OJSXHw8MDGRkZ6OzsxMmTJxdc3NHQTEsul09YEZyWONbV1UGv1yMsLMxsNsajvr4eixYtorLoAgC7d+/G+vXrqdmbDpbn0f+xefNmartggAtb9tra2qguiAgEAqxYsQLnz583y5R6OnmK9vb2SEtLo9rMimsubqbl7u6OlJSUSQsmmFscBwcHUVNTgxUrVlAVDKVSie7ubmrTaOBCUWJLS9MxYLHCeP311+PMmTPUErDt7e3h6emJpqYmKvYMuLu7QyKRmHxKPdN2BDExMcZmVhcng883DKPExsbGCZtpjYe5xFGv1+P06dMIDQ2lnkbV2NgIPz8/aj2xKysr0dTUhDVr1lCxN10sVhg9PT2RlZWFt956i5rNkJAQNDY2Uk++NvWUerY7WgzNrOzt7XH06FHOEtLNwejoKCoqKpCdnQ1XV1dkZmbOKEfQHOJYV1cHQgj1KbRGo4FUKqWaMvPmm29i8+bN1Fa/p4vFCiMAbN++HV9++SW1vcUeHh6cJF+bckptqm1+AoEAw6IA1FgHoqFNhsOHD6OxsZH6Pm9TodPpUFdXh59//hmDg4NIS0tDVFTUrIpBmFIcFQoFamtrERsbSz3mZkgipzVK1Wq12LNnD+677z4q9maCRQvj+vXrMTg4iEOHDlGzyVXytbu7O4KCglBcXDzjZHNT7n0ukPYj5c18/OFwM9b/MAg7/8Vobm7GkSNHOCmfNlO0Wi0aGxtx+PBhdHR0IDExEYmJiSar+2cKcdRqtSgpKUFoaCj1eoRcJJHv2rUL9vb2yMzMpGZzuli0MFpbW+PXv/413nnnHWo2uUq+Bi7kNlpZWeHMmTPTjjeauiDEsbpe8Hg86PQEPB4PJ7v0SE9PR1RUFJqamnDo0CFUVlZSy8OcLkNDQzh//jwOHTqEtrY2LFu2DKmpqWYpLjsbcSSE4PTp07C3t+dk22FVVRX1JPJ//etfuO+++6i1m50JFi2MAHDPPffg559/Rk9PDzWbtJOvDfD5fKxcuRK9vb3TSh0yR5WcrFB3EEJgxeeBEILMEHfweDz4+voiIyMDiYmJGB4extGjR3Hy5ElIpVJOtjhezMjICJqampCfn4/s7GxoNBokJycjNTXV7CW0ZiqO1dXVGBoaotqX2oBCoUB7eztVQa6vr0dBQQHuuusuajZnAo/MgZyMrKwsZGZm4plnnqFm88yZM9DpdIiNjaVm04BCoUBubi7i4+Mn7VdsztJhBdJ+ZNf3IjPEHUmS8RcoNBoNWltb0dHRgb6+PohEImMfaScnpylf7DqdDt999x3Wr18/5bgfIQQKhQIymQwymQwKhQIeHh7w8fGBn58ftRYAF9PX14eCggJER0dPWo2nra0N5eXlSE9P52QRorCwEE5OTlQr9zz66KNobm7GN998Q83mTJgTwvjNN9/gscceQ11dHbXKySqVCocPH0Z6ejonfSg6OjpQWlqK9PT0K05zLK2e4ujoKDo7OyGTydDZ2QkejweRSAQXFxe4uLhAJBLB3t5+3MWFyYRRr9dDqVRiYGAAcrkccrkcAwMDxoLDYrEYnp6enIjhWKYijnK5HPn5+UhISKBWZ/Fient7UVhYiKuvvpraZ6ZWqxEYGIgvv/wSV111FRWbM2VOCKNOp8OiRYvwyiuvYOvWrVd8XoG0H8fqepEVeuURznSoqKjA4OAgZ70oqqur0draivT09MtOXksTxbEQQjA0NGQUMMO/Wq0Wtra2sLW1hVAohFAoNHbaa2hoMKaMjI6OQq1WQ6VSQaVSQaPRQCAQXCKyLi4ucHBwsMhY1UTiqFKpkJOTg7CwMGo9XC6GEIK8vDz4+PhQrQr+1ltv4a233sL58+ct8ju7mDkhjADwyiuvYN++fcjLyxv3ccMqKo93ISaW/2DKrMVxdHQUP//8MxITE6l3hAMunMAlJSVQqVRISkoyCoili+KVIIRgdHTUKHYG4dPpdMZ0mtDQUFhZWcHKysoonBcLqKVfUBcznjiOjo4iPz8frq6uiImJ4eT9yGQynDlzBmvWrKHauyYmJgaPPPIItm3bRs3mTLH4xRcDv/nNb1BWVobTp0+P+/jYVdTs+tn3jLa2tsbixYtx7ty5aefvFUj78cKROhRIZ17Si8fjYcWKFbCyskJRURF0Ot2cFUXgwvuxsbGBs7MzvLy8EBAQgLCwMISHhyM8PBwAjH+HhYUhICAAnp6ecHJygrW19ZwSReDyBZnR0VGcPHkSjo6OnImiTqfD+fPnER4eTlUUDx8+jNbWVvzyl7+kZnM2zBlhdHV1xe23345XXnll3MfHW0U1BcHBwSCETCvp2zB6/fNP1Uh5M39W4mhlZYWEhAQQQnD8+HFUVlbOSVFcqBjEsby8HDk5ObC1teVkBdpAVVUVhEIhAgMDqdp99dVXsX37duqNxGbKnBFGAHjkkUewf/9+tLW1XfZYksQV+Q+m4G/rlphkGm2Az+cjNjYWNTU1U+4NY+rRq5WVFcRiMYaGhuDk5EQ154wxe5ycnGBvbw+lUgmxWMxZNZm+vj40NTVh+fLlVIW5uroa2dnZeOCBB6jZnC1zShjDw8Nx3XXX4cknnxz38SSJK/54VajJRNGAs7MzQkNDUVpaOqUptalHr42NjaipqUFqaiqsrKxQWFg4b/Yuz3cM02cHBwckJyfj/PnznLRJ0Ol0KC0tRUREBPXUoP/5n//B7bffDj8/P6p2Z8OcWXwxUFtbi+XLl6OkpMQYl6KBXq9HTk4OfH19p5QQO5UcwKkwNqao0+lw6tQpaLVaJCQkwMbGZsbHtiRmksdo6ahUKhQUFMDR0dG4B3o6eY6m5Pz585DL5UhOTqY6WiwsLMTq1atRU1MDX19fanZny5waMQJAWFgYbr/9djz++ONU7Rqm1LW1tVOaUpti9DreQosh5mhnZ4ecnBxqrV8Z06O/vx/Hjx+Hq6sr4uLijNNnLtokcDWFBoA//vGPeOihh+aUKAJzUBgB4JlnnsGRI0dQUFBA1e50p9SzYaLVZ4NIBwUFIS8vDzKZzKy+MKZHa2srTpw4gSVLlmDZsmWXiRFNcTRMoSMjI6lPoX/88UeUlZVRH8SYgjkpjD4+Pnj44Yc5+cDDwsJACEFdXZ3ZbEwlJYfH4yE0NBRxcXE4ffo0amtr533FbUuHEIKKigqcPXsWiYmJE1bDpiWOhlVompW5gQuhpyeffBJPPvkkXFxcqNo2BXNSGAHg8ccfR3l5Ob7//nuqdqc7pZ4u081T9Pb2RlpaGqRSKU6fPj1nyoHNN0ZHR1FUVITOzk5kZGTAw8Nj0teYWxy5nEJ/8cUX6OzsxEMPPUTVrqmYs8IoEonw1FNP4amnnqJePNXZ2RlLlixBUVERNBqNyY470+RtJycnpKenQ61WIzc3F4ODgybziTE5AwMDyM3NBY/HQ1paGuzt7af8WnOJo0qlwqlTp7B06VLqU2itVotnn30Wzz333KQ9dCyVOSuMAPDggw+iq6sLn3/+OXXbISEhcHV1RXFxsUmEebY7WmxsbJCUlASxWIycnBxjmXyG+dDr9aiqqkJeXh4CAwMRHx9v3LY5HUwtjjqdDkVFRfDx8YFEIpn18abLm2++CT6fjzvuuIO6bVMxp4VRKBTiueeew7PPPks9r4/H42H58uUYHR3F+fPnZ3UsU23z4/P5CA8PR2pqKlpaWtjo0YwMDAwgJycHXV1dSE9PR2ho6Kymq6YSR0IIzpw5AysrKyxdunTGx5kpw8PDeOWVV/Diiy/O6EfCUpjTwggAd9xxBwQCAd544w3qtg2pM21tbTNuZGWOvc8ikQgZGRnw8vJio0cTo9frUV1djby8PPj5+SEtLQ1OTk4mObYpxLG+vh69vb2Ij4/nZIfNiy++CD8/P4ttizpV5lyC93js3bsXv/3tb1FXV8dJwc++vj6cPHkSSUlJ06rCQ6MghFwuR2lpKQQCAZYtW8ZJbcmpMBcSvOVyOcrKysDj8RAbG2syQRzLTJPAOzs7UVxcjLS0NE6+597eXoSFheHbb79FRkYGdfumZF4IIyEEWVlZCA0NxQcffMCJDy0tLTh//jzS09OnFHynWSVHr9ejpqYGdXV18PX1RXh4+LQWCGhgycI4NDSEyspKdHV1YfHixQgJCTH7aGy64jg4OIjc3FzExsZCLBab1bcr8Ytf/AKjo6PYu3cvJ/ZNyZyfSgMX4n0ffvgh/vOf/+Cnn37ixIeAgAAEBASgqKho0ngn7dJhhtijof7e0aNHce7cOc57tFg6KpUKZ86cQXZ2Nuzs7LBmzRqEhYVRmaJOZ1qt0WhQWFiI0NBQzkRx9+7dOHLkCHbu3MmJfVMzL4QRuFAe7IUXXsD27duhVCo58SEyMhK2trYoLS29YkyPy3qKQqEQy5YtQ2ZmprF1Q3V1NStIMYbR0VFUVFTgyJEj0Ol0uOqqq7B06VLY2tpS9WMq4qjX61FSUgIXFxeEhYVR9c9Ab28vHnroIbz55pucCbOpmRdTaQN6vR5ZWVkICwvjbEo9OjqKvLw8uLq6XrYdzNKKzMrlclRUVEChUCA4OBgSiYT6xW/AEqbSKpUKTU1NaGhogLu7OyIiIiwiJnulabWhwvvw8DCSk5M5WwW+6aabjFPouVZM+ErMK2EELojPsmXLsHv3blxzzTWc+KBWq5GXlwcvLy8sXboUPB7P4kTxYnp6elBfX4/u7m74+fkhODiYuo9cCmN/fz8aGhrQ0dEBb29vhISEwM3NjaoPkzFWHAkhKC0thUKhQEpKCmdNwL766its374dFRUV82a0CMxDYQSAt99+G6+88grOnTvHySo1cKHHcX5+Pnx8fGBnZ4fq6mqLFMWLUSqVaGhoQHNzM5ycnCCRSODn50dlJEJbGEdHR9Ha2gqpVIrh4WFIJBIEBwdbdIVpgzguXboU/f396OvrQ0pKCmel53p6erB06VLs2LEDt956Kyc+mIt5KYx6vR6rV6/GokWL8OGHH3Lmx/DwMLKzs6HT6ZCWljZnNtNrtVq0t7dDKpVCoVAY+0R7eXmZbWRCQxg1Gg26urqMfahdXV0hkUjg4+NjcSvhV6K3txcnTpyAjY0NMjMzOQt9AMCNN94InU6Hb7/9dt5MoQ3M3dT0CeDz+fjoo48QExODrVu3cjal7uzsBHBhu15bWxtEItGcOIEEAgECAwMRGBiIwcFBtLe3o76+HqdPn4a7u7tRKC0t5Wc8hoaGjL2u+/r64OLiArFYjPDw8DnXIoIQgpaWFtja2mJ0dBRdXV1Ui91ezH/+8x8cO3YMlZWVc+Kcni7zUhgBICgoCC+99BLuu+8+nDt3jvpFYIgpGqY6+fn50Ol0iI6OnlMnkpOTE5YsWYIlS5ZApVIZRaaiogIODg7w8vKCq6srXFxcYGdnx+l7I4RgeHgYcrkc/f396OzsxMjIiLEj4cqVKzkdYc0GvV6PsrIyDAwMICMjA0ql0liPlLY49vT04JFHHsHbb78Nb29vqrZpMS+n0gYIIVi9ejWCgoKoTqnHW2hRqVTIz8+Hu7v7uMVL5xparRY9PT3o7u6GXC7HwMAArKysIBKJ4OLiAhcXF4hEItjb20/pvU53Kk0IgVKpxMDAAORyudEHQojRB09PT3h4eMyZafKV0Ov1OH36NIaGhpCcnGyMKXLVJuGGG24AIQTffPPNnD+Pr8S8FkYAkEqliI6Oxr///W9s2rTJ7PYmWn1Wq9U4efIk7OzsEBsby9lKojkghGBwcPAyodLr9bC1tYVQKLzkZriPz+eDx7vQNOzkyZNYtWqV8f86nQ5qtRoqlQoqleqyv8cTYkdHx3l1sWo0GhQXF0On0yEpKemyc4a2OH788cd47LHHUFFRMW9Hi8ACEEbgQjzkt7/9LfLz8xEREWE2O1NJyRkdHcXp06ehVCqRmJjI2ao5DQghUKvVlwjaWJHT6/UghECv1xvbw/J4PPB4PFhZWV0iqmMF1sbGZl6J4FgUCgWKiorg6uqK5cuXX3HkS0sci4qKsGbNGuzatQvXXXed2exYAgtCGAHgqaeewu7du1FUVGSW1eHp5CkSQlBVVYWmpibEx8dPqdrzfMcSErwtCZlMhtOnTyMsLGxKJc3MLY4ymQzx8fF4+OGH8Yc//MHkx7c0Foww6vV6bN68GUNDQ/j5559NevHNNHm7ra0NZWVliIyMxKJFi0zmz1yECeMFDP2EamtrERcXN63pqrnEUaPRIC0tDWFhYfj3v/89r0fpBubNXunJ4PP5+PzzzyGTyfDII4+Y7Liz2dHi5+eHlJQU1NTU4MyZM9RbNDAsC51Oh9OnT0MqlSItLW3aMTxztUn49a9/Db1ejw8++GBBiCKwgIQRuJB68t133+GLL77Ae++9N+vjmWKbn4uLCzIyMqBQKHDixAlW8WaBMjIygry8PKjVaqSnp8+41qOpxfGll17CkSNHsG/fvjnbv2UmLChhBC5U4dmzZw9+//vfIzc3d8bHMeXeZ6FQiOTkZDg4OCAnJwf9/f2zOh5jbtHT04Pjx48bRW22W/xMJY4HDx7E3/72N+zfvx++vr6z8mmuseCEEQCysrLwwgsv4Oabb57RiWOOghBWVlZYvnw5QkNDceLECVRUVLBWqPMcrVaL8vJyFBUVITIyEtHR0Sar9ThbcayqqsIdd9yBnTt3Ij4+3iQ+zSUWzOLLWAgh2L59OwoLC1FQUDDlaQKNKjlKpRKlpaXQaDRYsWIFXF1dzWLHklhoiy89PT0oKyuDg4MDli9fbrbiFTNZkBkYGEB8fDy2bNmCl156ySx+WToLVhiBC6ttq1evhqenJ77++utJf61plg4jhKChoQFVVVVYtGgRlixZMq8FY6EIo1arRWVlJVpaWhAVFYXAwECzL2hMRxx1Oh2uueYa2NjY4MCBA/P6u5iIBTmVNmBjY4NvvvkGxcXFeOKJJyZ8Lu16ijweDyEhIcjIyEBfXx+OHz/OYo9znN7eXmRnZ2NoaAhZWVmQSCRUVnmnM61+4IEH0Nrail27di1YUQTmcRGJqeLp6Ykff/wR6enpcHBwwNNPP33Zc7gsMuvo6IiUlBQ0NDTgxIkTC2L0SJMCaT+O1fUiK9QdSRLzhCwMo8Tm5mYsXbqUyihxLAZxnKjwxP/7f/8PBw4cQF5enkVULueSBS+MwIVeLUeOHEFWVhbs7Owuyey3hMrbhtGjt7c3SktLkZ2djaioKHh7ey+YvDJzUCDtR8qb+Rf2Zv9IkP9giknFkRCC9vZ2YyWirKwsTku1TSSOTz75JD7//HPk5eUt+M0GABNGI8uWLcOhQ4ewZs0a2Nra4uGHH7YIUbwYR0dHpKamorm5GeXl5bCzs0NkZOS0elkz/suxul7weDzo9ARWfB6y63tNJoxdXV2oqKiAVqtFREQE/Pz8LOJHbDxxfPbZZ/Hee+8hJyeHs4ZalgYTxotYuXIlvv/+e6xbtw4qlQrR0dEWI4oGeDweJBIJ/P390djYiMLCQotq3DSXyAp1B/nxgigSQpAZMvsfmIsbjC1ZsgQSiYRKu9XpcLE4fvDBB3j99deRnZ2NyMhIrl2zGJgwjiE5ORkHDhzA9ddfj+effx7XXnst1y6Ni5WVFUJDQyGRSFBXV4fc3Fz4+PggPDx8TlTWtgSSJK7IfzAF2fW9yAyZXYxxaGgIlZWV6OrqQmhoKBISEjjr2jcV3NzcUFxcjB07duDIkSNYtmwZ1y5ZFJb7zXFIRkYGDh48iA0bNgCASfdWmxpra2tERERg0aJFqK6uxtGjRyGRSLB48eI5W62aJkkS11kJokqlQnV1NVpaWhAUFGQMxVg6zz77LF577TUcPXoUK1eu5Nodi4MJ4xXIyMjATz/9hGuvvRYqlQqPP/441y5NiFAoxLJlyxASEoKqqiocPnwYAQEBCA4OnnO9TeYCCoUCDQ0NaG1tha+vL6666ipORuozWVV/4okn8P777yM7O5uNFK8AE8YJWLVqFY4cOYKrr74aKpUKzzzzDNcuTYqjoyNWrlwJhUKBxsZGZGdnw8PDA8HBwfD09LSIBYC5CiEEnZ2dqK+vh1wuR2BgIDIzMzn74ZnJqvrvfvc7fPnll8jJyWExxQlgwjgJcXFxyM7OxurVqzE4OIiXX37Z4oLp4+Hs7Ixly5YhIiICUqkUZWVlsLKygkQiQUBAwJyY7lkKKpUKzc3NkEqlAC4UIklISOC8NcV0VtV1Oh22b9+OH374Abm5uWz1eRKYME6BmJgY5Obm4rrrrkN1dTV27do1Z1oS2NjYICwsDCEhIejs7IRUKkVVVRXEYjEkEgk8PDzYKHIcCCHo6uqCVCpFV1cXvLy8EBMTAy8vL4v5vKa6qi6Xy3HDDTdAJpMhLy8PQUFBdB2dg8zbvdLffvstsrOzceuttyIpKckkx+zr68NNN92Erq4u7Nu3DyEhISY5Lm1GRkYglUrR3NwMQoixTzSXHfUsYa+0VqtFd3c3ZDIZOjs7jSPswMBAi61FWCDtn3BV/fz589i8eTPCw8PxxRdfzLjO40Jj3gpjXl4eUlNTTX7c0dFR/L//9//wn//8B19++SVWr15tchu0IISgv78fMpkMMpkMw8PD8PLyglgshre3N9XpNlfCODIyYuyV3dPTAwcHB+MPhYuLi8WMDmfCvn37cPfdd+O3v/0tnnvuuVmFgN5//31UVVVhZGQE//jHP8xWDchSmLfCWFFRAb1ej+rqaojFYuTk5ECn0yEtLQ0ZGRmzPv57772Hxx57DM8//zwefvhhE3jMPUql0iiSfX19cHFxgbe3N8RisbF7n7mgJYyEECgUCuP7VCgU8PDwML7P+ZID+vzzz+PFF1/EBx98gK1bt876eF1dXXB3dzfG2+c78zbGeOrUKdx555347LPP8OSTT0Kr1UKpVJpEFAHg3nvvRUREBLZs2YKzZ8/inXfeseiE3qng4OCAkJAQhISEQKPRoKurCzKZDLW1teDz+Zf0b3ZxcYGdnZ1Fj6gIIRgeHr6s17UhfBAWFgZPT0/OF1FMiVqtxp133onc3FxkZ2cjLi7OJMf18vLC119/jXXr1iE/P9/kAw1LY25fyRMQFRWF4uJixMbGorm5GTY2NsjPzzdpP9y0tDSUlJRgw4YNyMrKwt69e+fNvmUbGxv4+/vD398fer0eg4ODRmGpq6vDwMAABALBJULJpVgaRPBiAZTL5dDr9Ub//P39sXTpUrOPfrmira0NmzZtgkAgQElJCcRisUmPX15ejptuugnR0dEmH2hYGvNWGA3Z/IacPmdnZ4SHh5vcjkQiwcmTJ3H77bcjPj4e33777bxLmuXz+RCJRJfsGdfr9RgaGjIKUX19PQYGBgAAtra2EAqFEAqFl/x98X02NjZTEidCCDQaDVQqlfGmVqvH/T+PxzMKdUBAAKKjo+Ho6DgvRXAsBQUFuOmmm7B69Wq89957ZokPK5VKADDbQMOSmLcxRtro9Xo899xz2LFjB9555x3ceuutXLtEHULIJYI1nogZ7gMuFMTg8/ng8Xjg8XgYHR2FtbU1CCEghECv14MQAh6PB1tbW9ja2sLOzu4ysb34/wtBBMfy/vvv47HHHsOzzz6LRx991OyfgWGg0d/fP2/bbjBhNDHffvstfv3rX2PNmjXYuXPnvJlamxLDKNAggIQQjI6OIjs7G1lZWRAIBEax5PP5sLa2XpCCNxkymQzbtm3DqVOn8Nlnn2Ht2rVcuzRvsPwtHDPEcGFd6WYutmzZgsrKSqjVaixduhS7d+82m625imEEKBQKYWdnB3t7e+O2OgcHB9jb28POzg5CoXDKU+6Fxscff4zo6Gg4OzujsrLS7KLI1fXEFfNWGC8ejezevRsfffTRJfeZE7FYjH379uGVV17Bvffei5tvvhl9fX1mtclYGMhkMqxfvx7/8z//g/fffx9ffvkllVkJl9cTF8xbYTRQWlqKmJgY6nZ5PB5+9atfoaKiAsPDw1i6dCm++eYb6n4w5g+ffvqpcUGpsrISmzdvpu4DV9cTbebtqrSB7u5u1NfXo76+nhP7Pj4+OHDgAD777DNs27YNX331FXbu3AkXFxdO/GHMPTo7O/Gb3/wGBQUFePfdd3HDDTdw5gvX1xMt5v2Ice3atRgdHcXQ0BBnPvB4PNx+++2oqKjA0NAQoqKi8O2333LmD2Pu8NlnnyE6Ohr29vaorKzkVBQBy7ieaMBWpSlDCMG///1vPPzww1i3bh3eeusttnINyygiYUnIZDLcd999OHHiBOejxIXIvB8xWho8Hg933HGHMfYYFhaGP/3pTxgeHubaNYYFoFAo8Nhjj2Hx4sWws7OziFHiQoQJI0f4+vpi//792Lt3Lw4fPozQ0FDs2LEDWq2Wa9cYHKBWq/G///u/CAkJwenTp3HkyBHs2rULHh4eXLu2IGHCyDHp6ek4efIk3nnnHbz33nsIDw/Hv//9b+j1eq5dY1BAr9fj3XffxeLFi/Gf//wHn3/+OY4ePYr4+HiuXVvQMGG0AHg8HjZt2oRz587hqaeewhNPPIG4uDh8//33XLvGMCO7d+/G0qVL8dJLL+Gll15CaWkp1q5dOy8TpucaTBgtCCsrK9x9992oq6vDbbfdhttuuw0ZGRkoKCjg2jWGCcnOzkZSUhIeeOABPPDAA6iqqsItt9wyJ3oJLRTYN2GBCIVC/P73v0dTUxNSU1Nx9dVXY9OmTaisrOTaNcYsKC0txTXXXIONGzdi/fr1aGhowAMPPAAbGxuuXWOMgQmjBSMSifD888+jpqYGvr6+iI2NxdVXX42DBw9SiUEWSPvxwpE6FEj7zW5rvqLX67Fnzx5kZGQgJSUFkZGRaGhowJ/+9CfW79uCYcI4B/Dx8cE777yDxsZGJCcn46677sLSpUuxY8cOs6X5GHoW//mnaqS8mc/EcZooFAq88MILWLx4MR566CFcc801aG5uxo4dO9hK8xyACeMcQiwW49lnn0Vrayv++Mc/4pNPPkFgYCDuv/9+VFVVmdTWxT2LebwLPYsZk3PmzBls27YNgYGB2Lt3L55//nlIpVI8+eSTTBDnEEwY5yC2tra44447UFpair1796K/vx8rVqxASkoKPvjgA2Mh2NmQFeoOQibvWcwAhoeH8eabbyI+Ph7JycnQarU4dOgQCgsLsXXr1nnVU2ahwLYEzhN6e3vx6aef4t1330VPTw+2bNmC7du3z6oZ0mQ9i03JXNsSqNfrUVhYiJ07d2L//v0IDAzE9u3b8ctf/vKSFhCMuQkTxnkGIcRYhWXPnj1wd3dHZmYmbrjhBqxbt85iV0DngjCq1Wp899132Lt3L44dO4bBwUHccsstuPfee03WjY9hGTBhnMdoNBocP34c+/fvx759+6BQKJCWlob169fjpptusqjiFZYqjJ2dnfjqq6/w/fffIz8/H+7u7tiyZQs2btyIlJQUNk2epzBhXCAQQnDu3Dns27cPe/fuxdmzZxEXF4e1a9filltuMUsHxelgScJ49uxZ7Nq1Cz///DPKysoQFxeHzZs3Y+PGjQgPD2c7UxYATBgXKB0dHTh48CD27t2LI0eOICAgAFlZWUhJSUFKSgpCQkKo+sOVMOr1etTX1yM3Nxf5+fnIzs6GTCbD2rVrsXnzZlx33XXw9PSk5g/DMmDCyMDw8DCOHDmC77//HoWFhTh37hxEIhEiIiIQHR2N+Ph4pKamIjg42Gzb1mgIo16vR01NDfLz83Hq1CmcO3fOWP4tJiYGiYmJuP7665GZmQmhUGgWHxhzAyaMjMvQaDSoqKhASUkJiouLcerUKZw9exZOTk4IDw83imVaWhpCQkJMIpamFka9Xo/q6mrk5eUZRbCqqgojIyOIiYlBfHw8Vq5cibi4OEREREAgmPddPhjTgAkjY0qMjo6OK5bW1tbw8PCAp6en8ebj4wNfX18EBgYiICAAEokErq6uEwroVIVRr9ejr68PTU1NaG5uRmtrK9rb29HR0YGuri50d3ejp6cH3d3dIIRcJoLh4eFMBBmTwoSRMWNGR0fR3NyMjo4O462trQ3t7e1GsZLJZOjv74dQKDQKp6OjIwQCAaysrIz/WllZYWRkBHZ2dtDpdNDpdNBqtdBqtdDpdFAoFOjp6UFPTw9UKhXc3NwgFovh6+t7yc0gyj4+PggMDOR8IYcxN2HCyDA7arUaMpnMKJ4KhQJarRajo6NG8TPcBAKB8WZtbW38WyQSGQXP29vbYvMxGfMDJowMBoMxBrZXmsFgMMbAhJHBYDDGwISRwWAwxsCEkcFgMMbAhJHBYDDGwISRwWAwxsCEkcFgMMbAhJHBYDDGwISRwWAwxsCEkcFgMMbAyowwOKW4uBgKhQJnzpzB7373O67dYTAAsBEjg2OqqqqQnJyMhoYGrl1hMIywIhIMzvnuu+8gFAphZ2eHnJwc6HQ6pKWlISMjg2vXGAsUNpVmcMrzzz+Pp556Chs2bMDnn38OrVYLpVLJRJHBKWwqzeCUuLg45OfnIzY2Fs3NzbCxsUFZWRnXbjEWOGwqzbAYFAoFnJ2d0d/fD1dXV67dYSxgmDAyGAzGGFiMkcEpkzWvZ7/bDC5gMUYGpxBCQAjBW2+9BUIIfv/73xvvY6LI4AomjAyL4P7770dXVxckEgnXrjAYTBgZlgEhBEePHsWdd97JtSsMBhNGhmXw2muv4cSJE9i5cyfXrjAYbFWawWAwxsJGjAwGgzEGJowMBoMxBiaMDAaDMQYmjAwGgzEGJowMBoMxBiaMDAaDMQYmjAwGgzEGJowMBoMxBiaMDAaDMQYmjAwGgzEGJowMBoMxBiaMDAaDMYb/D0/Ny2tQlP1wAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "L, M, N = 6, 3, 1\n", + "lg = LinearGrid(L, M, N)\n", + "qg = QuadratureGrid(L, M, N)\n", + "cg = ConcentricGrid(L, M, N)\n", + "\n", + "plot_grid(lg)\n", + "plot_grid(qg)\n", + "plot_grid(cg)" + ] + }, + { + "cell_type": "markdown", + "id": "d345a34a-5a36-4f14-a897-4eb9c4b056b7", + "metadata": {}, + "source": [ + "Regarding node placement, the only difference between `QuadratureGrid` and `LinearGrid` is that `QuadratureGrid` does not evenly space the flux surfaces.\n", + "\n", + "As can be seen above, although the `ConcentricGrid` has nodes evenly spaced on each $\\theta$ curve, the number of nodes on each $\\theta$ curve is not constant.\n", + "On `ConcentricGrid`s, the number of nodes per $\\rho$ surface decreases toward the axis.\n", + "The number of nodes on each $\\theta$ surface is also not constant and will change depending on the `node_pattern` as documented [here](https://desc-docs.readthedocs.io/en/latest/notebooks/basis_grid.html#Concentric-grids).\n", + "\n", + "### Caution on different grid types\n", + "These differences between the grid types regarding the spacing of surfaces are important to consider in the context of certain computations.\n", + "For example the correctness of integrals or averages along a given surface will depend on the grid type.\n", + "If a grid does not evenly space each $\\theta$ surface, then some $\\theta$ surfaces will be assigned a different \"thickness\" (i.e. a larger d$\\theta$).\n", + "A flux surface average on such a grid would then assign more weight to nodes on some $\\theta$ coordinates than others.\n", + "This may introduce an error to the computation of an average as some locations on the surface would have more weight than others." + ] + }, + { + "cell_type": "markdown", + "id": "3a028514-fb91-452a-ae65-d67db2dabd50", + "metadata": {}, + "source": [ + "## Structure of computed quantities\n", + "\n", + "The number of nodes in any given grid is stored in the `num_nodes` attribute.\n", + "The grid object itself is a `num_nodes` $\\times$ 3 numpy array.\n", + "That is `num_nodes` rows and 3 columns.\n", + "Each row of the grid represents a single node.\n", + "The three columns give the $\\rho, \\theta, \\zeta$ coordinates, respectively, of any node.\n", + "\n", + "All quantities that are computed by DESC are either a global scalar or an array with the same number of rows as the grid the quantity was computed on.\n", + "For example, we can think of `nodes` as a vector which is the input to any function $f(\\text{nodes})$.\n", + "The output $f(\\text{nodes})$ is a vector-valued function which evaluates the function at each node.\n", + "See below for a visual." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "80b2df38-2b33-4483-8f9e-6ed697dd6b5c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " grid nodes ψ B\n", + "[0.212 0.000 0.000] [0.007] [3.407e-18 3.533e-01 3.572e-02]\n", + "[0.212 2.094 0.000] [0.007] [0.047 0.318 0.060]\n", + "[0.212 4.189 0.000] [0.007] [-0.047 0.318 0.060]\n", + "[0.591 0.000 0.000] [0.056] [-1.022e-17 3.830e-01 -4.195e-02]\n", + "[0.591 2.094 0.000] [0.056] [0.136 0.378 0.050]\n", + "[0.591 4.189 0.000] [0.056] [-0.136 0.378 0.050]\n", + "[0.911 0.000 0.000] [0.132] [-2.360e-17 2.629e-01 -7.165e-02]\n", + "[0.911 2.094 0.000] [0.132] [0.225 0.371 0.060]\n", + "[0.911 4.189 0.000] [0.132] [-0.225 0.371 0.060]\n", + "[0.212 0.000 0.110] [0.007] [-0.027 0.365 -0.010]\n", + "[0.212 2.094 0.110] [0.007] [-0.065 0.325 -0.010]\n", + "[0.212 4.189 0.110] [0.007] [-0.048 0.374 -0.073]\n", + "[0.591 0.000 0.110] [0.056] [0.059 0.414 0.053]\n", + "[0.591 2.094 0.110] [0.056] [-0.032 0.309 0.045]\n", + "[0.591 4.189 0.110] [0.056] [-0.030 0.378 -0.128]\n", + "[0.911 0.000 0.110] [0.132] [0.177 0.421 0.149]\n", + "[0.911 2.094 0.110] [0.132] [-0.032 0.231 0.030]\n", + "[0.911 4.189 0.110] [0.132] [-0.063 0.370 -0.225]\n", + "[0.212 0.000 0.220] [0.007] [ 0.027 0.365 -0.010]\n", + "[0.212 2.094 0.220] [0.007] [ 0.048 0.374 -0.073]\n", + "[0.212 4.189 0.220] [0.007] [ 0.065 0.325 -0.010]\n", + "[0.591 0.000 0.220] [0.056] [-0.059 0.414 0.053]\n", + "[0.591 2.094 0.220] [0.056] [ 0.030 0.378 -0.128]\n", + "[0.591 4.189 0.220] [0.056] [0.032 0.309 0.045]\n", + "[0.911 0.000 0.220] [0.132] [-0.177 0.421 0.149]\n", + "[0.911 2.094 0.220] [0.132] [ 0.063 0.370 -0.225]\n", + "[0.911 4.189 0.220] [0.132] [0.032 0.231 0.030]\n" + ] + } + ], + "source": [ + "eq = get(\"HELIOTRON\")\n", + "grid = QuadratureGrid(L=2, M=1, N=1, NFP=eq.NFP)\n", + "data = eq.compute([\"B\", \"psi\"], grid=grid)\n", + "\n", + "print(\" grid nodes \", \"ψ\", \" B\")\n", + "for node, psi, b in zip(grid.nodes, data[\"psi\"], data[\"B\"]):\n", + " print(node, \" \", np.asarray([psi]), \" \", b)" + ] + }, + { + "cell_type": "markdown", + "id": "8a0dc378-334b-4dea-8420-6bf8787be13b", + "metadata": {}, + "source": [ + "The leftmost block are the nodes of the grid.\n", + "\n", + "The middle block is a flux surface function.\n", + "In particular $\\psi$ is a scalar function of the coordinate $\\rho$.\n", + "We can see $\\psi$ is constant over all nodes which have the same value for the $\\rho$ coordinate.\n", + "\n", + "The rightmost block is the magnetic field vector.\n", + "The columns give the $\\rho, \\theta, \\zeta$ components of this vector.\n", + "Each row is the evaluation of the magnetic field vector at the node on that same row." + ] + }, + { + "cell_type": "markdown", + "id": "5b8d87ad-0c81-4646-8669-42c1083e1c45", + "metadata": {}, + "source": [ + "### Node order\n", + "The nodes in all predefined grids are also sorted.\n", + "The ordering sorts the coordinates with the following order with decreasing priority: $\\zeta, \\rho, \\theta$.\n", + "As shown above, this means the first chunk of a grid represents a zeta surface.\n", + "Within that $\\zeta$ surface, the first chunk represents the intersection of a $\\rho$ surface and $\\zeta$ surface (i.e. a $\\theta$ curve).\n", + "Then within that are the nodes along that $\\theta$ curve." + ] + }, + { + "cell_type": "markdown", + "id": "93fcd130-536b-4903-bbb6-60ec9af96165", + "metadata": {}, + "source": [ + "## Node weight invariants\n", + "\n", + "Each node occupies a certain volume in the computational domain which depends on the placement of the neighboring nodes.\n", + "Nodes with larger volume occupy more of the computational domain and should therefore be weighted more heavily in any computation that evaluates a quantity over multiple nodes, such as surface averages.\n", + "\n", + "All grids have two relevant attributes for this.\n", + "The first is `weights`, which corresponds to the volume differential element $dV$ in the computational domain.\n", + "The second is `spacing`, which corresponds to the three surface differential elements $d\\rho$, $d\\theta$, and $d\\zeta$." + ] + }, + { + "cell_type": "markdown", + "id": "d81ab673-f90d-4628-93a8-c924aee19c0c", + "metadata": {}, + "source": [ + "### Node volume\n", + "If the entire computational space was represented by 1 node, this node would need to span the domain of each coordinate entirely.\n", + "The node would need to cover every\n", + "- $\\rho$ surface from 0 to 1, so it must occupy a radial length of $d\\rho = 1$\n", + "- $\\theta$ surface from 0 to 2$\\pi$, so it must occupy a poloidal length of $d\\theta = 2\\pi$\n", + "- $\\zeta$ surface from 0 to 2$\\pi$, so it must occupy a toroidal length of $d\\zeta = 2\\pi$\n", + "\n", + "Hence the total volume of the node is $dV = d\\rho \\times d\\theta \\times d\\zeta = 4\\pi^2$.\n", + "If more nodes are used to discretize the space, then the sum of all the nodes' volumes must equal $4\\pi^2$.\n", + "We require\n", + "$$\\int_0^1 \\int_0^{2\\pi}\\int_0^{2\\pi} d\\rho d\\theta d\\zeta = 4\\pi^2$$" + ] + }, + { + "cell_type": "markdown", + "id": "cf7c3e7b-3952-438f-8f81-42bd9e1c2bc4", + "metadata": {}, + "source": [ + "### Node areas\n", + "Every $\\rho$ surface has a total area of\n", + "$$\\int_0^{2\\pi}\\int_0^{2\\pi} d\\theta d\\zeta = 4\\pi^2$$\n", + "Every $\\theta$ surface has a total area of\n", + "$$\\int_0^{1}\\int_0^{2\\pi} d\\rho d\\zeta = 2\\pi$$\n", + "Every $\\zeta$ surface has a total area of\n", + "$$\\int_0^{1}\\int_0^{2\\pi} d\\rho d\\theta = 2\\pi$$\n", + "\n", + "If S is the set of all the nodes on a particular surface in a given grid, then the sum of each node's contribution to that surface's area must equal the total area of that surface." + ] + }, + { + "cell_type": "markdown", + "id": "9457dbf2-fcea-48f0-9db5-d18787c99639", + "metadata": {}, + "source": [ + "#### Actual area of a surface\n", + "\n", + "You may ask:\n", + "> The $\\zeta$ surfaces are disks in the computational domain. Shouldn't any integral over the radial coordinate include an area Jacobian of $\\rho$, so that $\\int_0^{1}\\int_0^{2\\pi} \\rho d\\rho d\\zeta = \\pi$?\n", + "\n", + "If we wanted to compute the actual area of a $\\zeta$ surface, we would weight it by the area Jacobian for that surface in our geometry:\n", + "$$\\int_0^1 \\int_0^{2\\pi} \\lvert \\ e_{\\rho} \\times e_{\\theta} \\rvert d\\rho d\\theta$$\n", + "\n", + "When we mention \"node area\" in this document, we are just referring to the product of the differential elements in the columns of `grid.spacing` for the row associated with that node. For a $\\zeta$ surface the unweighted area, which is the sum of these products over all the nodes on the surface, would be $$\\int_0^1 \\int_0^{2\\pi} d\\rho d\\theta = 2\\pi$$\n", + "\n", + "That is an invariant the grid should try to keep. That way when we supply a Jacobian factor in the integral, whether that be for an area or volume, we know that the integral covers the entire domain." + ] + }, + { + "cell_type": "markdown", + "id": "88087129-a655-435d-af35-8c6740e76529", + "metadata": {}, + "source": [ + "### Node thickness / lengths\n", + "\n", + "We require\n", + "$$\\int_0^{1} d\\rho = 1$$\n", + "$$\\int_0^{2\\pi} d\\theta = 2\\pi$$\n", + "$$\\int_0^{2\\pi} d\\zeta = 2\\pi$$\n", + "where the integrals can be over any $\\rho$, $\\theta$, or $\\zeta$ curve.\n", + "\n", + "These are the invariants that `grid.py` needs to maintain when constructing a grid." + ] + }, + { + "cell_type": "markdown", + "id": "46838670-4052-441d-a00e-fd2c6326900b", + "metadata": {}, + "source": [ + "### Visual: `grid.weights` and `grid.spacing`\n", + "\n", + "Let's see a visual of the `weights` and `spacing` for `LinearGrid`.\n", + "Recall that `LinearGrid` evenly spaces every surface, and therefore each node should have the same volume and area contribution for every surface." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "48b02295-15f6-46ca-aa5a-5ff8e4bef6f4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Notice the invariants mentioned above are maintained in the examples below.\n", + "\n", + "The most basic example: 2 node LinearGrid\n", + " grid nodes dV d𝜌 d𝜃 d𝜁\n", + "[0.000 0.000 0.000] [19.739] [0.500 6.283 6.283]\n", + "[1.000 0.000 0.000] [19.739] [0.500 6.283 6.283]\n", + "\n", + "\n", + "A LinearGrid with only 1 𝜁 cross section\n", + "Notice the 𝜁 surface area is the sum(d𝜌 X d𝜃): 6.283185307179585\n", + " grid nodes dV d𝜌 d𝜃 d𝜁\n", + "[0.000 0.000 0.000] [4.386] [0.333 2.094 6.283]\n", + "[0.000 2.094 0.000] [4.386] [0.333 2.094 6.283]\n", + "[0.000 4.189 0.000] [4.386] [0.333 2.094 6.283]\n", + "[0.500 0.000 0.000] [4.386] [0.333 2.094 6.283]\n", + "[0.500 2.094 0.000] [4.386] [0.333 2.094 6.283]\n", + "[0.500 4.189 0.000] [4.386] [0.333 2.094 6.283]\n", + "[1.000 0.000 0.000] [4.386] [0.333 2.094 6.283]\n", + "[1.000 2.094 0.000] [4.386] [0.333 2.094 6.283]\n", + "[1.000 4.189 0.000] [4.386] [0.333 2.094 6.283]\n", + "\n", + "\n", + "A low resolution LinearGrid\n", + " grid nodes dV d𝜌 d𝜃 d𝜁\n", + "[0.000 0.000 0.000] [1.462] [0.333 2.094 2.094]\n", + "[0.000 2.094 0.000] [1.462] [0.333 2.094 2.094]\n", + "[0.000 4.189 0.000] [1.462] [0.333 2.094 2.094]\n", + "[0.500 0.000 0.000] [1.462] [0.333 2.094 2.094]\n", + "[0.500 2.094 0.000] [1.462] [0.333 2.094 2.094]\n", + "[0.500 4.189 0.000] [1.462] [0.333 2.094 2.094]\n", + "[1.000 0.000 0.000] [1.462] [0.333 2.094 2.094]\n", + "[1.000 2.094 0.000] [1.462] [0.333 2.094 2.094]\n", + "[1.000 4.189 0.000] [1.462] [0.333 2.094 2.094]\n", + "[0.000 0.000 2.094] [1.462] [0.333 2.094 2.094]\n", + "[0.000 2.094 2.094] [1.462] [0.333 2.094 2.094]\n", + "[0.000 4.189 2.094] [1.462] [0.333 2.094 2.094]\n", + "[0.500 0.000 2.094] [1.462] [0.333 2.094 2.094]\n", + "[0.500 2.094 2.094] [1.462] [0.333 2.094 2.094]\n", + "[0.500 4.189 2.094] [1.462] [0.333 2.094 2.094]\n", + "[1.000 0.000 2.094] [1.462] [0.333 2.094 2.094]\n", + "[1.000 2.094 2.094] [1.462] [0.333 2.094 2.094]\n", + "[1.000 4.189 2.094] [1.462] [0.333 2.094 2.094]\n", + "[0.000 0.000 4.189] [1.462] [0.333 2.094 2.094]\n", + "[0.000 2.094 4.189] [1.462] [0.333 2.094 2.094]\n", + "[0.000 4.189 4.189] [1.462] [0.333 2.094 2.094]\n", + "[0.500 0.000 4.189] [1.462] [0.333 2.094 2.094]\n", + "[0.500 2.094 4.189] [1.462] [0.333 2.094 2.094]\n", + "[0.500 4.189 4.189] [1.462] [0.333 2.094 2.094]\n", + "[1.000 0.000 4.189] [1.462] [0.333 2.094 2.094]\n", + "[1.000 2.094 4.189] [1.462] [0.333 2.094 2.094]\n", + "[1.000 4.189 4.189] [1.462] [0.333 2.094 2.094]\n", + "\n", + "\n", + "A ConcentricGrid with only 1 𝜁 cross section\n", + "Notice the node which composes a 𝜌 surface by itself has more weight than any node on a surface with multiple nodes.\n", + "The method of assigning this weight will be discussed later\n", + " grid nodes dV d𝜌 d𝜃 d𝜁\n", + "[0.355 0.000 0.000] [23.687] [0.600 6.283 6.283]\n", + "[0.845 0.000 0.000] [3.158] [0.400 1.257 6.283]\n", + "[0.845 1.257 0.000] [3.158] [0.400 1.257 6.283]\n", + "[0.845 2.513 0.000] [3.158] [0.400 1.257 6.283]\n", + "[0.845 3.770 0.000] [3.158] [0.400 1.257 6.283]\n", + "[0.845 5.027 0.000] [3.158] [0.400 1.257 6.283]\n", + "\n", + "\n" + ] + } + ], + "source": [ + "def print_grid_weights(grid):\n", + " print(\" grid nodes \", \"dV\", \" d𝜌 d𝜃 d𝜁\")\n", + " for node, weight, spacing in zip(grid.nodes, grid.weights, grid.spacing):\n", + " print(node, \" \", np.asarray([weight]), \" \", spacing)\n", + " print()\n", + " print()\n", + "\n", + "\n", + "print(\"Notice the invariants mentioned above are maintained in the examples below.\\n\")\n", + "\n", + "print(\"The most basic example: 2 node LinearGrid\")\n", + "print_grid_weights(LinearGrid(L=1, M=0, N=0))\n", + "\n", + "lg = LinearGrid(L=2, M=1, N=0)\n", + "print(\"A LinearGrid with only 1 𝜁 cross section\")\n", + "print(\n", + " \"Notice the 𝜁 surface area is the sum(d𝜌 X d𝜃):\",\n", + " lg.spacing[:, :2].prod(axis=1).sum(),\n", + ")\n", + "print_grid_weights(lg)\n", + "\n", + "print(\"A low resolution LinearGrid\")\n", + "print_grid_weights(LinearGrid(L=2, M=1, N=1))\n", + "\n", + "print(\"A ConcentricGrid with only 1 𝜁 cross section\")\n", + "print(\n", + " \"Notice the node which composes a 𝜌 surface by itself has more weight than any node on a surface with multiple nodes.\"\n", + ")\n", + "print(\"The method of assigning this weight will be discussed later\")\n", + "print_grid_weights(ConcentricGrid(L=2, M=2, N=0))" + ] + }, + { + "cell_type": "markdown", + "id": "50e29eeb-5678-46a7-9ba2-ded3acea90a6", + "metadata": {}, + "source": [ + "## A common operation which relies on node area: surface integrals\n", + "\n", + "Many quantities of interest require an intermediate computation in the form of an integral over a surface:\n", + "$$integral(Q) = \\int_0^{2\\pi}\\int_0^{2\\pi} d\\theta d\\zeta \\ Q = \\sum_{i} d\\theta d\\zeta \\ Q$$\n", + "\n", + "The steps to perform this computation are:\n", + "1. Compute the integrand with the following element wise product.\n", + "Recall that the first two terms in the product are $d\\theta$ and $d\\zeta$.\n", + "Repeating a previous remark: we can think of `nodes` as a vector which is the input to a function $f(nodes)$.\n", + "In this case $f$ is `integrand_function`. The output $f(nodes)$ evaluates the function at each node.\n", + "```python\n", + "integrand_function = grid.spacing[:, 1] * grid.spacing[:, 2] * Q\n", + "```\n", + "2. Filter `integrand_function` so that it only includes the values of the function evaluated on the desired surface.\n", + "In other words, we need to downsample `integrand_function` from $f(nodes)$ to $f(nodes \\ on \\ desired \\ surface)$.\n", + "This requires searching through the grid and collecting the indices of each node with the same value of the desired surface label.\n", + "```python\n", + "desired_rho_surface = 0.5\n", + "indices = np.where(grid.nodes[:, 0] == desired_rho_surface)[0]\n", + "integrand_function = integrand_function[indices]\n", + "```\n", + "3. Compute the integral by taking the sum.\n", + "```python\n", + "integral = integrand_function.sum()\n", + "```\n", + "\n", + "To evaluate $integral(Q)$ on a different surface, we would need to repeat steps 2 and 3, making sure to collect the indices corresponding to that surface.\n", + "With grids of different types that can include many surfaces, this process becomes a chore.\n", + "Fortunately, there exists a utility function that performs these computations efficiently in bulk.\n", + "The code\n", + "```python\n", + "integrals = surface_integrals(grid=grid, q=Q, surface_label=\"rho\")\n", + "```\n", + "would perform the above algorithm while also upsampling the result back to a length that is broadcastable with other quantities.\n", + "\n", + "We may think of `surface_integrals` as a function, $g$, which takes the nodes of a grid as an input, i.e. $g(nodes)$, and returns an array of length `grid.num_nodes` which is $g$ evaluated on each node of the grid.\n", + "This lets computations of the following form be simple element wise products in code.\n", + "$$H = \\psi \\lvert B \\rvert \\int_0^{2\\pi}\\int_0^{2\\pi} d\\theta d\\zeta \\ Q$$\n", + "```python\n", + "H = data[\"psi\"] * data[\"|B|\"] * surface_integrals(grid=grid, q=Q, surface_label=\"rho\")\n", + "```\n", + "\n", + "Below is a visual of the output generated by `surface_integrals`." + ] + }, + { + "cell_type": "markdown", + "id": "f975ee87-9381-471a-9f2e-5808eb84f5e3", + "metadata": {}, + "source": [ + "### Visual: `surface_integrals`" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6eed2d3c-6a41-4a27-8a1a-e7af1aee07e3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Notice that nodes with the same 𝜌 coordinate share the same output value.\n", + " grid nodes 𝜌 surface integrals of |B|\n", + "[0.212 0.000 0.000] [13.916]\n", + "[0.212 2.094 0.000] [13.916]\n", + "[0.212 4.189 0.000] [13.916]\n", + "[0.591 0.000 0.000] [15.199]\n", + "[0.591 2.094 0.000] [15.199]\n", + "[0.591 4.189 0.000] [15.199]\n", + "[0.911 0.000 0.000] [15.153]\n", + "[0.911 2.094 0.000] [15.153]\n", + "[0.911 4.189 0.000] [15.153]\n", + "[0.212 0.000 0.110] [13.916]\n", + "[0.212 2.094 0.110] [13.916]\n", + "[0.212 4.189 0.110] [13.916]\n", + "[0.591 0.000 0.110] [15.199]\n", + "[0.591 2.094 0.110] [15.199]\n", + "[0.591 4.189 0.110] [15.199]\n", + "[0.911 0.000 0.110] [15.153]\n", + "[0.911 2.094 0.110] [15.153]\n", + "[0.911 4.189 0.110] [15.153]\n", + "[0.212 0.000 0.220] [13.916]\n", + "[0.212 2.094 0.220] [13.916]\n", + "[0.212 4.189 0.220] [13.916]\n", + "[0.591 0.000 0.220] [15.199]\n", + "[0.591 2.094 0.220] [15.199]\n", + "[0.591 4.189 0.220] [15.199]\n", + "[0.911 0.000 0.220] [15.153]\n", + "[0.911 2.094 0.220] [15.153]\n", + "[0.911 4.189 0.220] [15.153]\n" + ] + } + ], + "source": [ + "from desc.compute.utils import surface_integrals\n", + "\n", + "grid = QuadratureGrid(L=2, M=1, N=1, NFP=eq.NFP)\n", + "B = eq.compute(\"|B|\", grid=grid)[\"|B|\"]\n", + "B_integrals = surface_integrals(grid=grid, q=B, surface_label=\"rho\")\n", + "\n", + "print(\"Notice that nodes with the same 𝜌 coordinate share the same output value.\")\n", + "print(\" grid nodes \", \"𝜌 surface integrals of |B|\")\n", + "for node, B_integral in zip(grid.nodes, B_integrals):\n", + " print(node, \" \", np.asarray([B_integral]))" + ] + }, + { + "cell_type": "markdown", + "id": "202ac51d-e8ed-4bb7-992e-111b31e28f89", + "metadata": {}, + "source": [ + "## Grid construction\n", + "\n", + "As the above example implies, it is important that correct values for node spacing are maintained for accurate computations.\n", + "This section gives a high-level discussion of how grids are constructed in `grid.py` and how the invariants mentioned above for spacing and weights are preserved.\n", + "\n", + "The code is modular enough that the function calls in the `__init__` method of any grid type should provide a good outline.\n", + "In any case, the main steps are:\n", + "1. Massaging input parameters to protect against weird user inputs.\n", + "1. Placing the nodes in a specified pattern.\n", + "2. Assigning spacing and weight to the nodes based on placement of nodes and their neighbors.\n", + "4. Enforcing symmetry if it was specified by the user.\n", + "5. Post processing to assign useful things as attributes to the grid.\n", + "\n", + "`LinearGrid` is the grid which is most instructive to give a walk-through on.\n", + "The construction process for`QuadratureGrid` and `ConcentricGrid` are similar, with the only difference being that they place the nodes differently in the `create_nodes` function.\n", + "\n", + "There are two ways to specify how the nodes are placed on `LinearGrid`.\n", + "\n", + "The first method is to specify numbers for the parameters `rho`, `theta`, or `zeta` (or `L`, `M`, `N` which stand for the radial, poloidal, and toroidal grid resolution, respectively).\n", + "The second method is to specify arrays for the parameters `rho`, `theta`, or `zeta`." + ] + }, + { + "cell_type": "markdown", + "id": "a1c0f285-6d90-4b7f-8c3b-edcf15d67904", + "metadata": {}, + "source": [ + "### $\\rho$ spacing\n", + "\n", + "When we give numbers for any of these parameters (e.g. `rho=8`), we are specifying that we want the grid to have that many surfaces (e.g. 8 $\\rho$ surfaces) which are spaced equidistant from one another with the same $d\\rho$ weight.\n", + "Hence, each $\\rho$ surface should have $d\\rho = 1 / 8$.\n", + "The relevant code for this is below.\n", + "```python\n", + "r = np.flipud(np.linspace(1, 0, int(rho), endpoint=axis))\n", + "dr = 1 / r.size * np.ones_like(r)\n", + "```\n", + "\n", + "When we give arrays for any of these parameters (e.g. `rho=[0.125, 0.375, 0.625, 0.875]`), we are specifying that we want the grid to have surfaces at those coordinates of the given surface label.\n", + "In this case the surfaces are assigned equal thickness (i.e. $d\\rho$), but that is not always the case.\n", + "The rule to compute the thickness when an array is given is that the cumulative sums of d$\\rho$ are node midpoints.\n", + "In terms of how $d\\rho$ is used as a \"thickness\" for an interval in integrals, this is similar to a midpoint Riemann sum.\n", + "\n", + "In the above example, for the first surface we have\n", + "$$(d\\rho)_1 = mean(0.125, 0.375) = 0.25$$\n", + "For the second surface we have\n", + "$$(d\\rho)_1 + (d\\rho)_2 = mean(0.375, 0.625) = 0.5 \\implies (d\\rho)_2 = 0.25$$\n", + "Continuing this rule will show that each surface is weighted equally with a thickness of $d\\rho = 0.25$.\n", + "The algorithm for this is below.\n", + "```python\n", + "# r is the supplied array for rho\n", + "# choose dr such that cumulative sums of dr[] are node midpoints and the total sum is 1\n", + "dr[0] = (r[0] + r[1]) / 2\n", + "dr[1:-1] = (r[2:] - r[:-2]) / 2\n", + "dr[-1] = 1 - (r[-2] + r[-1]) / 2\n", + "```\n", + "\n", + "If instead the supplied parameter was `rho=[0.25, 0.75]` then each surface would have a thickness of $d\\rho = 0.5$.\n", + "An advantage of this algorithm is that the nodes are assigned a good $d\\rho$ even if the input array is not evenly spaced." + ] + }, + { + "cell_type": "markdown", + "id": "39f9a868-060e-4700-9193-c2ec21de2958", + "metadata": {}, + "source": [ + "#### An important point\n", + "This touches on an important point.\n", + "When an array is given as the parameter for $\\rho$, the thickness assigned to each surface is not guaranteed to be equal to the space between the two surfaces.\n", + "In contrast to the previous example, if `rho=[0.5, 1]`, then $(d\\rho)_1 = 0.75$ for the first surface at $\\rho = 0.5$ and $(d\\rho)_2 = 0.25$ for the second surface at $\\rho = 1$.\n", + "The first surface is weighted more because an interval centered around the node $\\rho = 0.5$ lies entirely in the boundaries of the domain: [0, 1].\n", + "The second surface is weighted less because an interval centered around the node at $\\rho = 1$ lies partly outside of the domain.\n", + "Since each node is meant to discretize an interval of surfaces around it, nodes at the boundaries of the domain should be given less weight.2\n", + "As half of any interval around a boundary lies outside the domain.\n", + "A visual is provided below.\n", + "\n", + "#### An analogy with a stick\n", + "Footnote [2]: If that explanation did not make sense, perhaps this analogy might.\n", + "Suppose you want to estimate the temperature of an ideal stick of varying temperature of length 1.\n", + "You can shine a laser at any location of the stick to sample the temperature there.\n", + "This sample is a good estimate for the temperature of the stick in a small interval around that point.\n", + "\n", + "You pick the center of the stick, $\\rho = 0.5$ for your first sample.\n", + "You record a temperature of $T_1$.\n", + "This is your current estimate for the temperature of the entire stick from $\\rho = 0 \\ to\\ 1$.\n", + "You decide to measure the temperature of the stick at one more location $\\rho = 1$, recording a temperature of $T_2$.\n", + "\n", + "Now to calculate the mean temperature of the stick, weighing the measurements equally and claiming $T$average $= 0.5 * T_1 + 0.5 * T_2$ would be a mistake.\n", + "Only the temperature of the stick at the midpoint of the measurements, $\\rho = 0.75$, is estimated equally well by either measurement.\n", + "The temperature of the stick from $\\rho = 0\\ to\\ 0.75$ is better measured by the first measurement because this portion of the stick is closer to 0.5 than 1.\n", + "Hence, a more accurate way to calculate the stick's temperature would be $T$average $= 0.75 * T_1 + 0.25 * T_2$" + ] + }, + { + "cell_type": "markdown", + "id": "6c5e4958-973c-4fdb-893b-55aac9f39ec8", + "metadata": {}, + "source": [ + "#### Visual: $d\\rho$ \"spacing\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "3f4b60b7-be21-4984-b0b5-ee3e6b7959e1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Both of these nodes have 𝑑𝜌=0.5\n" + ] + }, + { + "data": { + "image/png": "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cYCyytgWoYHtEDrYFqGB7RA6gtkWAemx0el06NOnT4+L6o6YmBh+YEME2yI0sB1CB9siNLAdQkcotAUHDxMREZFmMNgQERGRZoRssDEYDFi0aBEMBs46qza2RWhgO4QOtkVoYDuEjlBqix4NHiYiIiIKRSHbY0NERETUXQw2REREpBkMNkQaISIYP348FEXBpk2b1C6HiDSgubkZd955J/R6PVavXq12OV4JarBxOp1YtmwZRowYgdzcXIwYMQLLli1rc+Xwznz11VeYNm0ahg0bhqFDh2LatGn46quvAly1NvW0LRwOB9auXYsrrrgC+fn5yM3NxfDhwzFnzhzs378/SNVri6/fi1bPPfccPvvsswBVqX2+toPVakVxcTHOP/98jBkzBoMGDcK4cePwpz/9KcCVa48vbeF0OvHkk09i9OjRGDVqFEaOHIkJEybg1VdfDULl2rJx40bk5eXho48+gtvt7vbyqm2zJYjmzJkjffv2lX379omIyL59+6Rv374yZ86cLpfdtWuXxMfHy8KFC8Xtdovb7Zbf/e53kpCQILt27Qp06ZrT07bYunWrAJBly5aJ0+kUEZHKykoZM2aMJCQkyO7duwNeu9b48r1oVVtbK6mpqTJ9+nQBIBs3bgxQtdrlSzs0NTXJhAkT5JZbbpGmpiYREbHZbPLLX/5SCgoKAlq3FvnSFr/5zW9EURRZt26d575HH31UAMjjjz8esJq1aPz48bJlyxYpKSkRAFJSUuL1smpus4MWbFo3iGd+sJ544gkBIFu3bu10+csuu0xSUlLEbrd77rPb7ZKamipTp04NSM1a5UtbbN26VQYOHHjW/e+9954AkHnz5vm9Xi3z9XvRas6cOXL77bfLokWLGGx6wNd2WLhwoQwZMsQT9lvV1NTIRx995Pd6tczXtoiPj5dRo0a1e39+fr5fa9U6h8MhItKjYKPmNjtoh6JauwGnTJnS5v7LLruszePtqa2txb///W9cdNFFMBqNnvuNRiMuvvhibNiwAbW1tQGoWpt8aYuCggJ8++23Z92flZUFADh58qS/yowIvrRFq61bt2L9+vVYunSp/wuMEL60g91ux5///GdcffXV0Ov1bR5LTk7GhRde6Odqtc3X70R8fDwcDkeb+9xuN1wuF0wmkx8r1b6ezkmj9jY7aMHmyy+/BADk5OS0uT8nJweKomDH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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The left node has 𝑑𝜌=0.75, the right has 𝑑𝜌=0.25\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjYAAAC6CAYAAAC9Wwe/AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAolElEQVR4nO3deXhU5fk+8PvMkkkCk0B2CCEQwASRhAgYECSIgkgrYIVarNpKpbaoFLdaL9sKLQGx37qh0roB2traQl3an1qqYhGKokgCEUT2JYkhCQnZJrM+vz9iRgIJmWQyc5bcH665rnAmZ+Z535nMuec97zlHEREBERERkQGY1C6AiIiIqLsw2BAREZFhMNgQERGRYTDYEBERkWEw2BAREZFhMNgQERGRYTDYEBERkWEw2BAREZFhMNgQERGRYTDYEBERkWEw2BAREZFhMNiQ4WRlZWHy5MmYPHkyRo0aBUVRMG7cOP+yyZMnq11ip73zzjv+tuhBV+t94YUXkJWVhUGDBnX5ue+8807ceeedXV6/hdb6vLPt6o6+JNIlITKY/Px8/8+bNm0SAHL48OE279eTlrboRSD1PvTQQ+e8HmvWrJH09PQuP29DQ4M0NDR0ef0zaanPu9KuQPqyrdeASM8s6sYqou63YsWKoO4nfYuOjla7hJAwaruIuht3RZHhjB8//rz3v/zyyzCZTMjJycH7778PAPjxj3+MxYsXAwB27NiBkSNHYtiwYdi9ezcAoLy8HPPmzcPEiRMxYcIE3HbbbaitrW3z8c/cBbBq1SpMnToVGRkZeOmll1r9XkePuX//fkyePBljx47FzJkzUVhYeM763/ve9zBx4kRMnDgRixYtgsPh8N//6KOPIi8vD1OmTMFll12Gl19+ud0+efLJJzFlyhRceeWVGD9+PFavXt3p9nRU79lWr16NtWvXorCwEJMnT8YVV1zR6v72nut87X7qqacwaNCgVrsbz65/2rRpiIyMxNq1a8+pqTNtKCwsxLp167Bq1Sq43e5z7l+4cGGn3medbVdLrWPGjMHVV1+NgoICKIqCyZMno6SkJKC+7Og1INIltYeMiEKprV1RIiKXXnqpFBQUiIiIx+ORfv36SVpamv/+Bx98ULZt2yYiIl6vVy655BJZvHixiIj4fD6ZN2+ezJo1q93nXbNmjURERMi///1vERF55ZVXJCYmRjweT0CP6Xa7JTMzU+677z7//2fOnOnfLdKy/qJFi/xtmDlzptx5550iIrJ3716JjY0Vh8MhIiKfffbZeXc3PPbYY/7fbWpqkqFDh8rWrVsDbk9H9banvV1R7T1XR+3u6DE3bNggIiL/+Mc/5K233mr1O51pw+bNm+Xuu+8WEZGXXnpJli5d2mb7An2fdbZdbdU6Y8aMc2rt6HVrr7+I9IzBhgytvWDz2GOPSW5urv937r77bjGbzbJ9+3YREZk6dar4fD4REfnoo48EgHz55Zf+9Tdv3iwA5OjRo20+75o1ayQ2Ntb//7179woAKS0tDegxt2zZIgBk//79/vv/9re/+Tdcba2/fv16iYqKEp/PJ8ePH5eoqCh56qmnpLq6WkRE6urq2u2n9957T6ZPny4TJkyQ/Px8iY2NlUceeSTg9nRUb3vaCyF9+vTx//+LL77wP1dH7T7fY8bExJy3ls60YezYsXL69GkREfnnP/8pV1xxRZuPGej7rLPtCrTW8/VlCwYbMhrOsaEeac6cObj77rtx+PBhbNiwAYsWLUJRURE2bNiAyMhIjBgxwn80zNGjRwEAycnJ/vVbfj569CgGDhzY5nP06dPH/3NkZCQAwOl0BvSYpaWlAICkpCT//QkJCf6fW9b/0Y9+BJOpeY9yU1MTkpKSUFFRgQEDBmDr1q14/PHHsWTJEowePRpLlizBuHHjzqnz8OHDmDFjBp555hnMnz8fADB58mQ0NDQE3J4TJ06ct97Oio2N9f9ss9n8z9VRu898/rP17dv3vM8ZaBs+/PBD9OvXDzExMQCAoqKidp830PdZZ9vVmf5ury+JjIrBhnqkAQMGIC8vD3//+99x6NAhDBs2DHPmzMGjjz4Km82GuXPn+n83PT0dQPPcjpaNWXl5eav7OqujxzSbzQCAkydP+u+vrKz0r99yCO9f/vIXpKam+pefPHkSSUlJaGxsRFpaGtatWwen04kVK1Zg6tSpKC8vP2cS6o4dO+B0OnHttdf6l7lcrk61Jy0t7bz1dpeO2h2MQNvw3nvvYdKkSf7/v/baa/j973/f5mMG+j7rbLvC1d9EesTJw9RjzZkzB4888oh/FOM73/kODh06hNdff73VBOSxY8dizJgxeOaZZwAAIoLVq1fjmmuuaXe0piMdPWZeXh6GDx+OZ599FgDg9Xqxbt06//pjxoxBXl5eq0m+77//PmbPng0A2L59O+bPnw8Rgc1mw6RJk+B2u9s8J0tmZiYURcHmzZsBAEeOHEFRUVGn2tNRve2JjY1FXV0dAGDx4sX46KOPzvv7HbU7GIG2YcuWLfB4PACag8ill16K/Pz8dh83kPdZZ9vVVq1//vOfO99odP41INI8lXeFEYXMmjVrJCcnRwBIXl6evP32263uP3LkiACQPXv2+JddfvnlrSZstigrK5Prr79eJkyYIOPHj5dbb71Vampq2nzev/71r5KZmSk2m02uv/56KSsrk7y8PH8dLfMiOnrM/fv3S35+vowePVqmTZsmv/3tbwWA5Ofny4kTJ+Srr76SefPmyfjx4+Xyyy+XmTNnyrFjx/yPfeONN8r48eMlPz9fxo4dK2+88Ua7ffX444/LwIED5corr5QFCxZIdna2pKeny6pVqwJuT0f1tuXIkSMycuRImTBhglx99dUBPdf52r1q1SpJT0+X2NhYmTNnzjmvR35+vuzcubPdfuioDR6PR5KTk2Xr1q2ydu1a+dOf/tTuY53ZxkDeZ51t15m1futb35Lf//73rebYBPq6nf0aNDU1ddgmIi1TRERUylRERLry6aef4v7778d7772ndimorKxsNa/m1VdfxV133eWfn0XUU3FXFBFRgLZs2YIJEyaoXQYA4JZbbsHOnTsBAG63G2vWrMGsWbNUropIfZw8TEQUoJSUFEybNk3tMgAAs2fPxoIFC2C321FXV4exY8fikUceUbssItVxVxQREREZBndFERERkWEw2BAREZFhdCnYiAgcDge4F4uIiIi0pEvBpqmpCdHR0WhqaurueoiIiIi6jLuiiIiIyDAYbIiIiMgweB4bIvLzOX3wNnghHgEEUCwKTFEmmKPNapdGZHgenw91Xi88IvABsCgKIhQFvc3mNq/zRm0LKNi43W7/Rd8AwOFwhKwgIgotT70H9TvrUbejDg27GtB0pAnOE064yl3w1nrbXMcUbYI10Qpbqg2R6ZGIvjAa9tF22EfbEZEUEeYWEOmX0+dDcUMDPq2rQ2F9PQ47HDjudOIrlwvVHg/aOiQnQlGQaLWiv82GgTYbMqOjMdpux2i7HQNtNoaeswR0gr4lS5Zg6dKl5yxvbGxEVFRUSAojou7hc/pQ80ENKt+sxKl/n0LToSb/aIyIAG1nmfaZvl7X1fzRYU22os/kPkiYlYC4q+Ng7WPt/kYQ6ZRXBNtra/FmVRX+X1UV9jY2wiMCMwCTosDdhaOLIxQFrq/XizWbMT42FrPi43FNQgJSbbZuboH+BBRs2hqxiY+PZ7Ah0iify4fK1ytx8q8nceqdU/A5fFCsCsQdmlM0KBYF4hXABMReGovE7yYi+cZkhhzqkUQE79fU4M/l5Xi9shLVHg+sXQwxgWiZLOsDkN2rF+YkJuKHKSlIi4wMyfNpXZcuqeBwOBAdHc1gQ6QxTcebUPZsGUpWl8BzytP8idfZEZlgKfCP6iTfmIzU21Nhz7WHuQii8Ktxu7GuvBxPnjiBQ01NsCgKPCqc782iKPCK4Nvx8ViUmoopffvC1IN2VzHYEBlA45eNOPzLw6hYXwHFrDRP/tWAllEie54dGQ9noO/kvmqXRNTtKlwuFBw9ij+UlsIjEvbvEu2xAPAAGBwZiWWDB+N7SUk9IuAw2BDpmLPUiSNLj6Ds+TIoJu0EmnOYAXiBvlP7ImNlBkdwyBDqPB48euIEVh47BreIKqMzgVAACIAR0dH4vyFDcFVcnKEnHDPYEOmQz+3DsYeP4eiyo4AP2g00Z1EszeEr6ftJGPbEMFjjOQeH9EdE8HJ5ORYfOIBaj0czIzQdMaF5Hs5lsbF4ITMTw6Kj1S4pJBhsiHSmvqgee27cg8Y9jc2fUjqkWBSYY83IfD4TibMT1S6HKGClTid+tG8f3jl1Su1SusyiKDABWJmRgUUDBhhu9xSDDZFO+Dw+HCs4hiO/PdI8tuzpaA2N+3p8PPH6RFzwzAWwxnH0hrSrZZTm9v370eTzaXa3U2coAPJiYvByVhaGGmj0hsGGSAfcp9wovq4Ypzef1u0oTXsUi4KIfhHIfjsbvUb0UrsconO4fD7cuX8/ni0rU7uUbmdRFEQqCtZfdBGuiotTu5xuwWtFEWlcw54GfJr7KWq31Bou1ADN84OcZU7sGLsDlf+sVLscolYqXC5cXliIFwwYagDAI4IGnw9X79qFR48fRxfGOjSHwYZIw6reqsKOsTvgLHXqZoJwl3gAX5MPxbOKcfTho4b4cCX9K66vx6hPP8X2ujrdTBDuCvn6ds/Bg/jBF1/A7dP3NygGGyKNOrn+JHbP3A2fw6f/+TSB+PrT9fADh3HogUMMN6Sqz+rqMGHnTpS7XIaYTxOoV8rLcW1xMZw6DjcMNkQadPJvJ7Hnu3uazxrccz5T/Y4/chwH7z3IcEOq2FFXh/ydO9Hg9Rp6pKYtXgDvnDqFmbt36zbcMNgQaUzlG5XYM29Pjww0fgKcePQEDv/qsNqVUA+zu74eUwoL4fD5elyoaeEF8F51NeZ8/rkud0sx2BBpSN2OOnz+3c97dqg5w7GCYyh9vlTtMqiHKHe5MK2oqEeO1JzNC+DtqircdeCA2qV0GoMNkUY4v3Ji17d2NV8lm8HG78uffImaLTVql0EG5/T5MHP3blTq6EzCoeYF8HRpKZ4t1deXCwYbIg3wOX0ovqYYnipP+K/GrXUCFM8sRtOxJrUrIYMSEdy2bx921NX1qInCgVr45ZfYXFOjdhkBY7Ah0oADiw+gbmedsQ/p7iof4K3zNh8h5tbf/n7SvufKyrCuvJzfKdohAGYXF6Pc5VK7lIAw2BCp7NS7p1D6h1KO1JyHeAQNuxtw7OFjapdCBnPE4cBiHc4jCScfgDqvFz/58ktdHKnIYEOkIk+dB1/c/AX/EgPhA44sPYL6XfVqV0IGISLNJ6TTwcZabR4RvF5Zib9VVKhdSof4cUqkooP3HoS7wm3ISyWEhALsvXEvd0lRt/hjaSk+PH2a82oCpAC4bd8+ze+SCijYuN1uOByOVjciCk7tp7Uoe7aM82o6wwM0fN7QvOuOKAhVbjfuPXiQByB2ggBo9PnwwKFDapdyXgEFm4KCAkRHR/tv8fHxoa6LyPAO3XcIsKhdhQ75gCMPHYGnridcZ4JC5eFjx+DkSE2nuUWw9quvsKehQe1S2qVIADOB3G43PJ5vPkQcDgfi4+PR2NiIqKiokBZIZESn3j2FXVN3qV2GfpmBQb8ehEG/HqR2JaRDx5uaMOTjjzm3possioIZcXF4Y+RItUtpU0AjNlarFVFRUa1uRNQ1IoKD9xwEzGpXomNe4NjDx+Cq0Pa+ftKmh44c4S6oIHhE8GZVFT46fVrtUtrEycNEYVb9bjUadjXw8O4giVtQ+gzn2lDnlDqdWPfVV5wwHCQLgOXHtHn6BQYbojArWVXCuTXdQDyCkqdLeIQUdcpzZWUwKYraZeieB8C/qqpwvEl7ZwRnsCEKo6bjTaj6V1XzpwIFzV3hRtWbVWqXQTrh9vnwdEkJR2u6iVlR8GxZmdplnIPBhiiMyp4tg2Lmt8VuYwZOPHlC7SpIJ96sqkKF2612GYbhEcEzJSVw+bQ1aspgQxQmIoKyF3nemm7lBU5vPo2mE9obDiftWVtWxjn73eyUx4P/VFerXUYrDDZEYdKwuwGuUh7F0+3MaN69R3QeDq8XG6urOWe/m1kVBW9WVqpdRisMNkRhUvlmJRQrd0N1OwEqX9PWBytpz3vV1XBxbk23c4vgtcpK+DTUtww2RGFSsaEC4tbOH79h+ICa92vgqeeMbGrfG1VVsPJoqJCocLvxWV2d2mX4MdgQhYGr0oWGQu2eglzvxCOo+aBG7TJIw/5VWckzDYeIVVHwzqlTapfhx2BDFAb1O+rVLsHQlAgFdZ9q5xsjaUu5y4WveDRUyHhEsJ0jNkQ9S92OOs6vCSFxC+o+0c4HK2nLDg1tdI1IAHxcW6t2GX4MNkRhUPdJHcTLYfCQEaBuOzde1LYddXWI4PyakDrpduOkSxtHfTLYEIVB7fZaQFvnsDIcd6Ubzq+capdBGvRpXR3n14SBViYQa/qKNc5SJzzVPNKB9E28AleZNr7JGF3Nphr0zu6tdhmacNLuQ2M8v7sCwOcNDbyad4hZFAVHndr4YqHZYOMsdeLjYR/D18ivuUQUmL037FW7BE2ojAdu/BPgjFS7EuopTGi+croWaDbOe6o9DDVERF1QZ2eoofDyASjlHBsiIiIyAo8ISjhiQ0REREahlaOiAppj43a74fF8M4nX4XCErKAzVaAC9eCJzYiIOqPEBeCw2lVQUHr3BhIT1a6iUzwaOfIsoGBTUFCApUuXhrqWVk7XncYNuAEe8KgoIqJOKQUwX+0iKCgWC/Daa80BRye0EmwUkY4raWvEJj4+Ho2NjYiKigpJYQ2fN+Cti97iiA0RUSeV9Ad+tUztKigoOhyxGdWrF3aOHat2GYGN2FitVlit1lDXco7Er/8REVEnRAAYrHYR1NNYTdqYtquNKoiIiEjXUiIi1C4BAIMNERERBcmqKBhgs6ldBgAGGyIiIgqSAqAfR2zOz9LXAlO0ZssjItIsex1ga1K7CupJvCLor5ERG81eK8rW34a8/Xm8CCYZQtH0IrhOaOPkVUaWuzUXlljNfqyF1V5eBNPv3gMH8O/qal4IM4S8AIZEauM6Hpr+BLD1t8HWXxsJkCgYfSb1wclXTzb/9VNI2AbYEHtprNplaAYPivrGFX374r2aGrg1cp4Vo8q129UuAYCGd0URGYl9jB2KoqhdhnGZAPs4bXyokvaMttsZakIs3WZDrEUbYyUMNkRhYB9th3j4wRoqillBzJgYtcsgjbpYIyMJRmUCMC5GO39/DDZEYdD74t6AWe0qjEvcAvsl3HhR22ItFmRoZP6HEZkUBXkMNkQ9i6W3BbETY5uPiaRuZ4o2IXYC59dQ+65NSICVu4NDwiOCb8XHq12GH4MNUZgkXpvIv7hQMAPxM+JhimDnUvuuSUjgPJsQGRwZiQuio9Uuw4+fBERhEn9NPI+KCgUfkDA7Qe0qSOMmxMTAbub+4O5mVRRcp7GLdTLYEIVJVEYUorO0863GMExA3NVxaldBGmcxmTArIQEW7o7qVm4RzNTQbiiAwYYorPrd1o+TiLuRYlGQMDsB1jir2qWQDvwoJQUe7o7qVoMjIzEhVlvz2xhsiMIo5QcpUMz8xthdxCNIvSNV7TJIJ/L79MHQqCi1yzAMM4BFqakwaWwUjMGGKIysfa1IvjEZilVbHwR6FTU0Cn3y+6hdBumEoij4WWoqN3zdxKwo+EFKitplnIOvL1GYpd6eCnFzODxoZiD1Z6k8ozN1yk0pKYgwcdMXLKui4MbkZPS1am83MF9dojCzX2xH36v6QrFwgxwMa5wV/eb3U7sM0plYiwV3DxjAqW5B8onggYED1S6jTQw2RCoY8sgQiJejNl2mAIOXDYY5mpsn6ryfDxyIXjz0u8ssioIF/ftjqIbOXXOmgIKN2+2Gw+FodSOiruud3RtJ85I416YrTEDkoEikzNfevn3Sh1iLBb8eNIjf7LvIDODX6elql9GugF7XgoICREdH+2/xGjtmnUiPBi8bDHDQpvN8QMbKDJgs3CxR193evz+SIiIYbjrJDOCetDT0s9nULqVdikjHB/W73W54PB7//x0OB+Lj49HY2IgoHjpH1GVHlx/F4V8dBnxqV6IPikVB3yv7YuRbIzlpmIL2VlUVvrV7t9pl6IYZQFpkJD4fOxbRGt6VF1BYtVqtiIqKanUjouCl/TwNvUb24kTiAJkiTch8IZOhhrrFjPh43JyczLMRB8gH4OWsLE2HGoCTh4lUZbKYMPxPw9UuQzeGPTUMtv7aHQIn/Xli6FDEWSzcGHag5WR8E/v0UbuUDvG1JFJZ74t6Y/DywQC/NLZLsSiIvyYeyTcnq10KGUwfqxUvDR/O6W7nYQEwOCoKyzMy1C4lIAw2RBqQdm8aEuckNn+CUCuKRUFkRiSG/2k4d0FRSFwVF4flgwfzu0UbTACizGa8PXKk5ndBtWCwIdIARVGQtTYLvS7kfJtWTIApyoTst7JhiWHqo9C5f+BAfDcxkd8t2vCPESM0e86atjDYEGmEOdqMkf8aCXOsmVcAP8NFr12EqCE8YIFCS1EUrMnKwkW9enEy8RkeHToUV8bFqV1GpzDYEGlIZFokct7NgTmK4QYKMPyl4eh7RV+1K6EeIspsxjs5ORhos/X4cKMAuD8tDYtSU9UupdMYbIg0xj7Kjpz3c2CKMvXccKMAmS9kIvn7nCxM4ZUcEYHNublIjYjoseFGQfMRUCsyMnQ5r43BhkiDYsbGIPeDXJh7mXvWhGKl+Za1Lgv9buEFLkkdqTYb/nfxxRjUQ0du7k1Lw2NDh+oy1AAMNkSaZR9tx8XbLoatv61nTCi2NJ+A76LXL0LKTbwOFKmr/9fh5hK7vUcMnH79nQL/N2QIVup0pKZFQJdUOJvD4UB0dDQvqUAUBu5TbhRfV4zTm08b9tILikVBRL8IZL+djV4jeqldDpGfy+fDHfv347myMrVLCRmLoiBSUfD3ESMw3QDXguSIDZHGWeOsyNmYg9SFX0/i0+8XqbaZgJjxMRizcwxDDWlOhMmEP15wAZ4eNgwmGG/am0VRkGaz4dMxYwwRagCO2BDpSsVrFdh36z54a70Qj77Pldqye23Qbwch7d40Xq2bNO+T2lrcuHcvDjoc8KpdTJBMaB4Avq1fP/xuyBDYLcaZzMdgQ6QzrkoX9t++HxV/q2gevdFjvjEBvXN6Y/jLwzlKQ7rS5PVi6dGjWHnsGEyALgOORVGQ9PWlJK7oa7zTKTDYEOlUxesVOLDoAJwlTt3MvVEsChSbgkFLBmHA4gEcpSHd+qS2Fj/98kvsqK/3j35onUVRICJYmJqKgsGDDTVKcyYGGyId87l8KHu+DId/fRieGo9mvz4qVgVQgAGLB2Dg/QNhjbOqXRJR0EQEr1dW4r6DB3GoqUmzg6cWRYFXBPOSkrBs8GAMNvh2m8GGyAA89R6UPFmCE4+dgLvSDc18hTQBpggTUn6YgoEPDkTkgEi1KyLqdh6fDy+Vl2PF0aM40NQEi6LA0/lNa7ezKAp8IpiVkIAlgwYhu3dvtUsKCwYbIgPxuX2ofKMSJU+W4PSHp6FYFYg7vB+wLc8ZOTQSA342ACk3pcASa8whb6IziQg+qKnBUyUleL2yEgqav1+E8y+wJVQlWK24vX9/LOjfH6k2WxgrUB+DDZFBNextQMXfK1CxoQINuxq+OblDd4/kKIBiViAegW2ADQnXJSDx2kTETorV9Um+iIJR6nTi7xUV+EdFBbacPg1B86HinhA8l1VR4BZBH4sFsxMSMDshATPi4mA19cw5bAEFG7fbDY/nm5fD4XAgPj6ewYZIJ5wlTlT9qwrV71Wj9qNaOI87AXwz90U8ElDg8f++q/ljwxJnQcwlMYidFIuEmQmIvjCaYYboLNVuN945dQobT53Cttpa7Hc44EPz1VJMX89/CWR6nFVRoABwi0AA9DKZcLHdjomxsfh2fDzyYmJg5t9fYMFmyZIlWLp06TnLGWyI9Mld40b9Z/WoL6qHs8QJV5kLTcea4CpxwVvvhXibg45iUaBEKrD1s8E20AZbfxsi+kWg14hesI+2I6J/BIMMUSc1er0oqq/Hzvp6HHc6Uep04rjTiRNOJ057PPCINAcfRYFVUZBstSItMhIDbDb0i4jAsKgojLHbkREVBRP//s7BERsiIiIyjIBm9FmtVlitPDyTiIiItK1nziwiIiIiQ+rSMZgte68cDke3FkNERER0PpGRkeed29elYNPU1AQAiDfIlUCJiIhIHzqa39ul89j4fD7U1NR0mJqC0TJBuaqqihOUQ4j9HHrs4/BgP4cH+zn02MfnF5IRG5PJhLi4uC4X1RlRUVF8YcOA/Rx67OPwYD+HB/s59NjHXcPJw0RERGQYDDZERERkGJoNNhaLBQ899BAsFl48L5TYz6HHPg4P9nN4sJ9Dj30cnC5NHiYiIiLSIs2O2BARERF1FoMNERERGQaDDRERERkGgw2RykQE48aNg6Io+OCDD9Quh4hCqLGxEXfddRfMZjPWrl2rdjmGFNZg4/F4sHz5clx44YXIzs7GhRdeiOXLl8Pj8QS0/q5duzBjxgxkZWUhMzMTM2bMwK5du0Jctf50tZ/dbjfWr1+Pa665BqNGjUJ2djaGDx+O+fPn4+DBg2GqXj+CfT+3eO655/Dxxx+HqEp9C7aPHQ4HCgoKcMkll2D06NEYMmQIxo4di9/97nchrlxfgulnj8eDJ598Erm5uRg5ciRGjBiB8ePH45VXXglD5fqyadMm5OTk4L///S98Pl+n1+c2MEASRvPnz5eUlBQ5cOCAiIgcOHBAUlJSZP78+R2uW1xcLHa7Xe6//37x+Xzi8/nk5z//ucTExEhxcXGoS9eVrvbztm3bBIAsX75cPB6PiIiUlpbK6NGjJSYmRvbs2RPy2vUkmPdzi8rKSklKSpKZM2cKANm0aVOIqtWnYPq4oaFBxo8fLz/+8Y+loaFBREScTqf88Ic/lLy8vJDWrTfB9PPtt98uiqLIhg0b/MseffRRASCPP/54yGrWo3HjxsnWrVtlzZo1AkDWrFkT8LrcBgYubMGmZaN59hv9iSeeEACybdu2865/1VVXSWJiorhcLv8yl8slSUlJMn369JDUrEfB9PO2bdtk8ODB5yx/5513BIAsXLiw2+vVq2Dfzy3mz58vd9xxhzz00EMMNmcJto/vv/9+GTZsmD+kt6ioqJD//ve/3V6vXgXbz3a7XUaOHNnm8lGjRnVrrXrndrtFRLoUbLgNDFzYdkW1DEtOmzat1fKrrrqq1f1tqaysxH/+8x9cfvnlsFqt/uVWqxVTpkzBxo0bUVlZGYKq9SeYfs7Ly8O+ffvOWZ6WlgYAqK6u7q4ydS+Yfm6xbds2vPXWW1i2bFn3F2gAwfSxy+XCH//4R1x77bUwm82t7ktISMCkSZO6uVr9Cva9bLfb4Xa7Wy3z+Xzwer2w2WzdWKn+dfWEe9wGdk7Ygs1nn30GAMjIyGi1PCMjA4qiYOfOne2uW1hYCJ/PhyFDhpxz39ChQ+Hz+VBUVNS9BetUMP2sKEqrP5oWe/bsAQBMnTq1GyvVt2D6GQC8Xi8WLlyIlStXIjY2NmR16lkwfbx7927U1NSgf//+WLJkCcaMGYPMzExMnDgRq1evhvC8pH7BvpeXL1+OAwcO4JlnnoHP54Pb7caDDz4Ip9OJ++67L2R19yTcBnZO2IJNRUUFbDbbOQnearUiKioKFRUV510XAGJiYs65r2XZ+dbvSYLp57aICFatWoW8vDzcdNNN3VmqrgXbz0899RR69+6Nm2++OZRl6lowfXzkyBEAwC9/+Us0NDRgy5Yt2LNnD2677TbccccduPXWW0NZuq4E+17+wQ9+gPXr12PlypWIj49HXFwcXnvtNbz77ru47rrrQll6j8FtYOeELdiICBRF6fK6ALq8fk8STD+3ZcWKFTh+/DjWr1/P65acIZh+Lisrw29+8xs8/fTT3VyVsQTTxw6HAwCQmJiIlStXIjIyEmazGTfddBPmzp2LF198kUeTfC3Yz4yCggLMnTsXDz/8MCorK1FdXY177rkHs2bNwquvvtqNlfZc3AZ2TtiCTVJSEpqamuB0Olstd7vdcDgcSExMPO+6AHD69Olz7qutrQWA867fkwTTz2d74oknsG7dOmzatAkDBgzo7lJ1LZh+vueee3DzzTcjOzs71GXqWjB93PItNjc3FyZT64+5vLw8AM1znCi4ft63bx9+9atfYe7cuZg3bx7MZjMsFgsWLFiA/Px83HLLLSgvLw91EwyP28DOCVuwufjiiwEAhw4darX80KFDEBHk5ua2u+6oUaNgMpnaPJfKgQMHYDKZkJOT070F61Qw/XymJUuW4KWXXsKWLVuQnp7e7XXqXTD9vGnTJmzcuBGjRo3y3/7whz8AAG699VaMGjUKv/jFL0JXvE4E08cXXXQRgOa5TGdrmUzclfOIGFEw/VxUVAQRQVZW1jn3DR8+HA6Ho8M5OtQxbgM7J2zBZt68eQCAd999t9XyjRs3AgBuuOEG/7KTJ0+2mmWfkJCAK6+8Eh988EGrE0Z5PB5s2rQJ06ZNQ0JCQijL141g+hloHvK888478cEHH2DTpk3+bwFlZWWYOXNmKEvXlWD6uaysDJ9//jkKCwv9t5/85CcAgOeffx6FhYV4+OGHQ90EzQumjzMyMpCbm4uioqJzAkzLhnb8+PEhqVtvgunnlJQUAN/MaTpTS1DiZ3PncRsYpHAeW37LLbdI//795eDBgyIicvDgQenXr1+rk0Bt3bpVzGazzJgxo9W6LScneuCBB/wnJ/rFL34hdrudJyc6S1f72eVyyQ033CCJiYny3HPPycsvv+y/PfbYY5Kenh7upmhaMO/ns/E8Nm0Lpo8//PBDsdlssnTpUv+yjRs3SkREhNxyyy3haYBOdLWfvV6vXHrppWKz2eT999/3L3/zzTfFbDbL5MmTxefzha8hOnG+89hwGxi8sAYbt9sty5Ytk6ysLBk5cqRkZWVJQUGB/6RFIiJFRUUSFxcnCxYsOGf9wsJCmT59umRmZsoFF1wg06dPl8LCwnA2QRe62s9vvPGGAGj3xmDTWrDvZ5Hmk6Dl5ORIcnKyAJAhQ4ZITk6ObN26NVzN0LRg+/h///ufTJ06VdLT02Xo0KGSnZ0tTz75pHi93nA2Q/OC6ee6ujopKCiQ7OxsGTFihAwfPlxycnJk5cqVUl9fH+6maNqyZcskJydH0tLSBICkpaVJTk6OrFixwv873AYGTxHhCR2IiIjIGHh1byIiIjIMBhsiIiIyDAYbIiIiMgwGGyIiIjIMBhsiIiIyDAYbIiIiMgwGGyIiIjIMBhsiIiIyDAYbIiIiMgwGGyIiIjIMBhsiIiIyDIvaBRBRz1JSUoK1a9fC5XIhPT0d8+fPV7skIjIQBhsiCpuioiL89Kc/xZtvvomEhARcddVVsNls+P73v692aURkENwVRURh0dTUhO985ztYtmwZEhISAACjR4/GmjVrVK6MiIyEwYaIwuLFF19EZGQkpkyZ4l926tQpVFZWqlgVERkNgw0RhcWrr76K2bNnt1r22WefYejQoeoURESGxGBDRCHncrmwfft2XHbZZf5lZWVl2LFjB+bOnatiZURkNAw2RBRyn3zyCZqamjBq1Cj/stWrVyMnJwdz5sxRrzAiMhwGGyIKuQ8//BBJSUnYtGkTgOajo9avX48NGzbAbDarXB0RGYkiIqJ2EURkbN/+9rcxdOhQ5Ofno7i4GACwcOFCxMfHq1wZERkNgw0RhZSIID4+Hi+88AKuvfZatcshIoPjrigiCqni4mLU1NRg0qRJapdCRD0Agw0RhdQnn3yC3Nxc7nYiorDgrigiCqm6ujo0NjYiOTlZ7VKIqAdgsCEiIiLD4K4oIiIiMgwGGyIiIjIMBhsiIiIyDAYbIiIiMgwGGyIiIjIMBhsiIiIyDAYbIiIiMgwGGyIiIjIMBhsiIiIyjP8Pyb19MvHVNHcAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "rho = np.linspace(0, 1, 100)\n", + "\n", + "print(\"Both of these nodes have 𝑑𝜌=0.5\")\n", + "figure, axes = plt.subplots(1)\n", + "axes.add_patch(plt.Circle((0.25, 0), 0.1, color=\"m\"))\n", + "axes.add_patch(plt.Circle((0.75, 0), 0.1, color=\"c\"))\n", + "axes.add_patch(plt.Rectangle((0, -0.0125), 0.5, 0.025, color=\"m\"))\n", + "axes.add_patch(plt.Rectangle((0.5, -0.0125), 0.5, 0.025, color=\"c\"))\n", + "axes.plot(rho, np.zeros_like(rho), color=\"k\")\n", + "axes.set_aspect(\"equal\")\n", + "axes.set_yticklabels([])\n", + "plt.title(\"Two nodes and their d\" + r\"$\\rho$\" + \" weight\")\n", + "axes.set_xlabel(r\"$\\rho$\", fontsize=13)\n", + "plt.xticks(fontsize=13)\n", + "plt.show()\n", + "\n", + "print(\"The left node has 𝑑𝜌=0.75, the right has 𝑑𝜌=0.25\")\n", + "figure, axes = plt.subplots(1)\n", + "axes.add_patch(plt.Circle((0.5, 0), 0.1, color=\"m\"))\n", + "axes.add_patch(plt.Circle((1, 0), 0.1, color=\"c\"))\n", + "axes.add_patch(plt.Rectangle((0, -0.0125), 0.75, 0.025, color=\"m\"))\n", + "axes.add_patch(plt.Rectangle((0.75, -0.0125), 0.25, 0.025, color=\"c\"))\n", + "axes.plot(rho, np.zeros_like(rho), color=\"k\")\n", + "axes.set_aspect(\"equal\")\n", + "axes.set_yticklabels([])\n", + "axes.set_xlabel(r\"$\\rho$\", fontsize=13)\n", + "plt.xticks(fontsize=13)\n", + "plt.title(\"Two nodes and their d\" + r\"$\\rho$\" + \" weight\")\n", + "plt.show()\n", + "\n", + "figure, axes = plt.subplots(1)\n", + "axes.add_patch(plt.Circle((0.5, 0), 0.05, color=\"m\"))\n", + "axes.add_patch(plt.Circle((0.85, 0), 0.05, color=\"r\"))\n", + "axes.add_patch(plt.Circle((1, 0), 0.05, color=\"c\"))\n", + "axes.add_patch(plt.Rectangle((0, -0.0125), 0.675, 0.025, color=\"m\"))\n", + "axes.add_patch(plt.Rectangle((0.675, -0.0125), 0.25, 0.025, color=\"r\"))\n", + "axes.add_patch(plt.Rectangle((0.925, -0.0125), 0.075, 0.025, color=\"c\"))\n", + "axes.plot(rho, np.zeros_like(rho), color=\"k\")\n", + "axes.set_aspect(\"equal\")\n", + "axes.set_yticklabels([])\n", + "axes.set_xlabel(r\"$\\rho$\", fontsize=13)\n", + "plt.xticks(fontsize=13)\n", + "plt.title(\"Three nodes and their d\" + r\"$\\rho$\" + \" weight\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6339d93b-09bc-41f6-a1f4-6e8238f190ac", + "metadata": {}, + "source": [ + "### $\\theta$ and $\\zeta$ spacing\n", + "\n", + "When a number is provided for any of these parameters (e.g. `theta=8` and `zeta=8`), we are specifying that we want the grid to have that many surfaces (e.g. 8 $\\theta$ and 8 $\\zeta$ surfaces) which are spaced equidistant from one another with equal $d\\theta$ or $d\\zeta$ weight.\n", + "Hence, each $\\theta$ surface should have $d\\theta = 2 \\pi / 8$.\n", + "The relevant code for this is below.\n", + "```python\n", + "t = np.linspace(0, 2 * np.pi, int(theta), endpoint=endpoint)\n", + "if self.sym and t.size > 1: # more on this later\n", + " t += t[1] / 2\n", + "dt = 2 * np.pi / t.size * np.ones_like(t)\n", + "```\n", + "\n", + "When we give arrays for any of these parameters (e.g. `theta=np.linspace(0, 2pi, 8)`), we are specifying that we want the grid to have surfaces at those coordinates of the given surface label.\n", + "\n", + "In the preceding discussion about $\\rho$ spacing, recall that even if a linearly spaced array is given as input for `rho`, $d\\rho$ may not always be the same for every surface, because we computed $d\\rho$ so that its cumulative sums were node midpoints.\n", + "The reason for doing this was because nodes which lie near the boundaries of $\\rho = 0, or\\ 1$ should be given less thickness in $d\\rho$.\n", + "For $\\theta$ and $\\zeta$ surfaces, the periodic nature of the domain removes the concept of a boundary.\n", + "This means any time a linearly spaced array of coordinates is an input, the resulting $d\\theta$ or $d\\zeta$ will be constant.\n", + "\n", + "The rule used to compute the spacing when an array is given is: $d\\theta$ is chosen to be half the cyclic distance of the surrounding two nodes.\n", + "In other words, if we parameterize a circle's perimeter from 0 to $2\\pi$, and place points on it according to the given array (e.g. `theta = np.linspace(0, 2pi, 4)`), then the $d\\theta$ assigned to each node will be half the parameterized distance along the arc between its left and right neighbors.\n", + "The process is the same for $\\zeta$ spacing.\n", + "A visual is provided in the next cell.\n", + "\n", + "The algorithm for this is below.\n", + "```python\n", + "# t is the supplied array for theta\n", + "# choose dt to be half the cyclic distance of the surrounding two nodes\n", + "SUP = 2 * np.pi # supremum\n", + "dt[0] = t[1] + (SUP - (t[-1] % SUP)) % SUP\n", + "dt[1:-1] = t[2:] - t[:-2]\n", + "dt[-1] = t[0] + (SUP - (t[-2] % SUP)) % SUP\n", + "dt /= 2\n", + "if t.size == 2:\n", + " dt[-1] = dt[0]\n", + "```\n", + "\n", + "An advantage of this algorithm is that the nodes are assigned a good $d\\theta$ even if the input array is not evenly spaced." + ] + }, + { + "cell_type": "markdown", + "id": "378f5e80-a393-41f5-a1fb-81ccce030d63", + "metadata": {}, + "source": [ + "#### Visual: $\\theta$ and $\\zeta$ spacing\n", + "\n", + "Here we are visualizing the $d\\theta$ spacing of a $\\theta$ curve (intersection of $\\rho$ and $\\zeta$ surface).\n", + "Let the node's coordinates be at the values given by the filled circles.\n", + "The $d\\theta$ spacing assigned to each node is the length of arc of the same color." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "3da00639-9c05-4076-8660-f6f5b4c183fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Each node is assigned a 𝑑𝜃 of 2𝜋/4\n" + ] + }, + { + "data": { + "image/png": "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XSt2yCzvdQP07t0bgNatW3P48GGys7PPCbbayhu+53U+wH18fFizZk2FnpOfn09ubi7R0dEMHjyYDRs2MHToUNatW8ell17qppq6nqPXvmLFCubPnw9wzus6+7VPmzaNe++9l4iICA4dOlQ60OWNevfuXTqIlZ+fT05ODs2bNyc1NZWGDRsyaNAgFi5cCEBSUhJt27bF39+/JqvsEuW97uXLl2O32xkyZAinT5/Gx8eHkJCQGq61+yilSEtL85rvuazELMeMGTOYOXMmQ4cOZcyYMXTp0oUPPviATZs2MWPGDIqKinj88ceJj49n//79vPjii7VqFkpoaCgHDhxg8uTJNGrU6JzXPm/ePJYtW0anTp3YtGkTkydPpkWLFjVd7Ur77LPP2LBhA7m5uYwaNYrGjRtz44038vvvvwN6RsKpU6dITk7moYce8rwZCZXk6HVv2rSJZ599ln79+rFr1y6uueaacseFvMnChQuZNWsWfn5+jB07lo4dO3rV91wCXAghvJQMYgohhJeSABdCCC8lAS6EEF5KAlwIIbyUBLgQQngpCXAhhPBSEuBCCOGlJMCFEMJLSYALIYSXkgAXQggv9f9R0hNvkg4otAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib.patches import Arc\n", + "\n", + "print(\"Each node is assigned a 𝑑𝜃 of 2𝜋/4\")\n", + "theta = np.linspace(0, 2 * np.pi, 100)\n", + "radius = 1\n", + "a = radius * np.cos(theta)\n", + "b = radius * np.sin(theta)\n", + "\n", + "figure, axes = plt.subplots(1)\n", + "axes.plot(a, b, color=\"k\")\n", + "axes.add_patch(plt.Circle((0, 1), 0.15, color=\"r\"))\n", + "axes.add_patch(plt.Circle((1, 0), 0.15, color=\"c\"))\n", + "axes.add_patch(plt.Circle((0, -1), 0.15, color=\"m\"))\n", + "axes.add_patch(plt.Circle((-1, 0), 0.15, color=\"b\"))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=45, theta2=135, color=\"r\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=-45, theta2=+45, color=\"c\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=-135, theta2=-45, color=\"m\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=135, theta2=225, color=\"b\", linewidth=10))\n", + "axes.set_aspect(1)\n", + "plt.title(\"Circumference 2$\\pi$\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "5cf2d2ec-4f85-4d73-90a7-332c4ed4499b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Non-uniform spacing\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"Non-uniform spacing\")\n", + "theta = np.linspace(0, 2 * np.pi, 100)\n", + "radius = 1\n", + "a = radius * np.cos(theta)\n", + "b = radius * np.sin(theta)\n", + "\n", + "figure, axes = plt.subplots(1)\n", + "axes.plot(a, b, color=\"k\")\n", + "axes.add_patch(plt.Circle((0, 1), 0.15, color=\"r\"))\n", + "axes.add_patch(plt.Circle((1, 0), 0.15, color=\"c\"))\n", + "axes.add_patch(plt.Circle((0, -1), 0.15, color=\"m\"))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=45, theta2=180, color=\"r\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=-45, theta2=+45, color=\"c\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=-180, theta2=-45, color=\"m\", linewidth=10))\n", + "axes.set_aspect(1)\n", + "plt.title(\"Circumference 2$\\pi$\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "ce65e824-a77c-48c1-bcf3-3f8a55662110", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Two nodes with symmetry set to false\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXAAAAF2CAYAAAB3QMMiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA7EAAAOxAGVKw4bAABY2UlEQVR4nO2dd3hU1daH30kljQAJJaEEQkeqlCioNBWxcAVRsaHXiqiIfmADFVEUFVEuCmIDFRW9YAErCuK1UENTOoQQSEJJIJAwKZNkf39sAoGcM5kkM8mU9T7PPJPMPvvMmpPJb9asvfZaFqWUQhAEQfA4/GraAEEQBKFyiIALgiB4KCLggiAIHooIuCAIgociAi4IguChiIALgiB4KCLggiAIHooIuCAIgociAi4IguChiIALgiB4KCLgbk5aWhqjRo2id+/e9O3blwsuuIAbbriBRYsWUVhYCMBDDz3EQw89VKXnmTx5Ms2bN6dfv35OsNoxyrP7/fffp127djRv3rxan7emGTNmDN26daNr1648+uijNW2OU1BKMXfuXPr27cuAAQPo1q0bo0aNIisrq6ZN82yU4LYkJyer2NhYNXv27NOPFRYWqhdeeEEBau/evUoppU6ePKlOnjxZ5ed79tlnVd++fat8Hkc5126j5587d66Ki4tz6fO6E7/++qsKCwtTVqtVFRUVqRkzZtS0SU4hOztbhYWFqR07diil9N8gISFBXXfddTVsmWcjHrgb8+CDD9KjRw9GjRp1+jF/f38mTJhA165dTz8WGhpKaGhoDVhYNWrKbne+Xvv27SM6OpqQkBD8/PwYM2ZMTZvkFAIDA5k0aRJt2rQB9N/glltu4fvvv0dJPb1KIwLuphw9epTvv/+eIUOGGI7//vvvNGvWjDfffPOs0EfpsMPMmTO5/PLLqVWrFvPmzePgwYPcdNNN9OnTh759+3LllVeycuVKUxsOHTrEiBEjuOiii7jooosYM2YMubm5hseOHj0aPz8/unTpwvLlywG49957GTt2LACJiYl06tSJ1q1b8/fff5exe/bs2cybN4+NGzfSr18/Bg4ceNb5Z86cyWWXXUZ8fDwfffSR3Wtn73U663rNmDHjrPNs3LiRrl27YrFYDM87cOBAWrduzU8//cTChQu56qqraNGiBR9//PFpu99//31eeuklDh48SL9+/Rg9enS5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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"Two nodes with symmetry set to false\")\n", + "theta = np.linspace(0, 2 * np.pi, 100)\n", + "radius = 1\n", + "a = radius * np.cos(theta)\n", + "b = radius * np.sin(theta)\n", + "\n", + "figure, axes = plt.subplots(1)\n", + "axes.plot(a, b, color=\"k\")\n", + "axes.add_patch(plt.Circle((0, 1), 0.15, color=\"r\"))\n", + "axes.add_patch(plt.Circle((1, 0), 0.15, color=\"c\"))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=45, theta2=225, color=\"r\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=-135, theta2=+45, color=\"c\", linewidth=10))\n", + "axes.set_aspect(1)\n", + "plt.title(\"Circle with circumference 2$\\pi$\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ea43241a-c0e6-4cc3-ab65-95b4929de5d0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The same two nodes with symmetry set to true\n", + "Notice now the red node is given more weight\n", + "because there is implicitly a duplicate of that node (in black) across the axis of symmetry.\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAVwAAAF2CAYAAAAiISB8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA7EAAAOxAGVKw4bAABW5klEQVR4nO2dd3hU1daH30klIYSEhF5C71VKQBQQBBQVGwqK5XptyJWmIHrxCnJFUbgoIqh8oqioSBEEsYMoShUJPfQAAhESCKS32d8fO4mBnDNp0zKz3uc5T+Dsvc+sM+U3e9Zeey2LUkohCIIgOBwfVxsgCILgLYjgCoIgOAkRXEEQBCchgisIguAkRHAFQRCchAiuIAiCkxDBFQRBcBIiuIIgCE5CBFcQBMFJiOAKgiA4CRFcF3H69GlGjBjB1VdfTe/evenevTt33303y5cvJycnB4BRo0YxatSocj3O1KlTadiwIX369LGD1SWjOLsXLFhAy5YtadiwoVMf19WMHj2aTp060bFjR5566ilXm2MXlFJ88MEH9O7dm759+9KpUydGjBhBUlKSq01zT5TgdOLi4lSdOnXU22+/XXAuJydHvfTSSwpQx44dU0oplZqaqlJTU8v9eJMnT1a9e/cu93VKypV2Gz3+Bx98oKKiohz6uO7ETz/9pCpXrqzS0tJUbm6umj17tqtNsgvJycmqcuXK6sCBA0op/RpER0erO++808WWuScyw3UBTz75JF26dGHEiBEF53x9fZk0aRIdO3YsOBccHExwcLALLCwfrrLbnZ+v48ePExkZSVBQED4+PowePdrVJtkFf39/pkyZQvPmzQH9GgwfPpyvv/4aJXmxiiCC62TOnz/P119/zeDBgw3bN2zYQIMGDXjrrbcucwUU/hk+Z84cBgwYQKVKlVi4cCHx8fHcc8899OzZk969ezNo0CA2bdpkasNff/3FsGHDuOaaa7jmmmsYPXo06enphn1HjhyJj48PHTp0YN26dQA89thjjB07FoDt27fTrl07mjVrxu7du4vY/fbbb7Nw4UJiYmLo06cP/fr1u+z6c+bMoX///jRu3JiPPvrI5nNn6z7t9XzNnj37suvExMTQsWNHLBaL4XX79etHs2bN+O6771i2bBk33XQTjRo14uOPPy6we8GCBbzyyivEx8fTp08fRo4cWezrYMv+0owze25t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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The same two nodes with symmetry set to true\")\n", + "print(\"Notice now the red node is given more weight\")\n", + "print(\"because there is implicitly a duplicate of that node (in black) across the axis of symmetry.\")\n", + "theta = np.linspace(0, 2 * np.pi, 100)\n", + "radius = 1\n", + "a = radius * np.cos(theta)\n", + "b = radius * np.sin(theta)\n", + "\n", + "figure, axes = plt.subplots(1)\n", + "axes.plot(a, b, color=\"k\")\n", + "axes.add_patch(plt.Circle((0, 1), 0.15, color=\"r\"))\n", + "axes.add_patch(plt.Circle((1, 0), 0.15, color=\"c\"))\n", + "axes.add_patch(plt.Circle((0, -1), 0.15, color=\"k\"))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=45, theta2=180, color=\"r\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=-45, theta2=+45, color=\"c\", linewidth=10))\n", + "axes.add_patch(Arc((0, 0), 2, 2, theta1=-180, theta2=-45, color=\"r\", linewidth=10))\n", + "axes.set_aspect(1)\n", + "plt.title(\"Circle with circumference 2$\\pi$\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fe14e7f0-ad7f-4181-acd4-bcb8d87df6e9", + "metadata": {}, + "source": [ + "## Symmetry\n", + "\n", + "For many stellarators we can take advantage of [stellarator symmetry](https://w3.pppl.gov/~shudson/Papers/Published/1998DH.pdf).\n", + "When we set stellarator symmetry on, we delete the extra modes from the basis functions.\n", + "This makes equilibrium solves and optimizations faster.\n", + "\n", + "Under this condition, we may also delete all the nodes on the collocation grid with $\\theta$ coordinate > $\\pi$.3\n", + "Reducing the size of the grid saves time and memory.\n", + "\n", + "The caveat is that we need to be careful to preserve the node volume and node area invariants mentioned earlier.\n", + "In particular, on any given $\\theta$ curve (nodes on the intersection of a constant $\\rho$ and constant $\\zeta$ surface), the sum of the $d\\theta$ of each node should be $2\\pi$.\n", + "(If this is not obvious, look at the circle illustration above.\n", + "The sum of the distance between all nodes on a theta curve sum to $2\\pi$).\n", + "To ensure this property is preserved, we upscale the $d\\theta$ spacing of the remaining nodes.\n", + "The upscale factor is given below.\n", + "$$d\\theta = \\frac{2\\pi}{\\text{number of nodes remaining on that } \\theta \\text{ curve}} = \\frac{2\\pi}{\\text{number of nodes on that } \\theta \\text{ curve}} \\times \\frac{\\text{number of nodes on that } \\theta \\text{ curve}}{\\text{number of nodes on that } \\theta \\text{ curve} - \\text{number of nodes to delete on that } \\theta \\text{ curve}} $$\n", + "The term on the left hand side is our desired end result.\n", + "The first term on the right is the $d\\theta$ spacing that was correct before any nodes were deleted.\n", + "The second term on the right is the upscale factor.\n", + "\n", + "For `LinearGrid` this scale factor is a constant which is the same for every $\\theta$ curve.\n", + "However, recall that `ConcentricGrid` has a decreasing number of nodes on every $\\rho$ surface, and hence on every $\\theta$ curve, as $\\rho$ decreases toward the axis.\n", + "This poses an additional complication because it means the \"number of nodes to delete\" in the denominator of the rightmost fraction above is a different number on each $\\theta$ curve.\n", + "\n", + "After the initial grid construction process described earlier, all grid types have a call to a function named `enforce_symmetry()` which\n", + "1. identifies all nodes with coordinate $\\theta > \\pi$ and deletes them from the grid\n", + "2. properly computes this scale factor for each $\\theta$ curve\n", + " - The assumption is made that the number of nodes to delete on a given $\\theta$ curve is constant over $\\zeta$.\n", + " This is the same as assuming that each $\\zeta$ surface has nodes patterned in the same way, which is an assumption\n", + " we can make for the predefined grid types.\n", + "3. upscales the remaining nodes' $d\\theta$ weight\n", + "\n", + "Specifically, we upscale the $d\\theta$ spacing of any node with $\\theta$ coordinate not a multiple of $\\pi$, (those that are off the symmetry line), so that these nodes' spacings account for the node that is their reflection across the symmetry line." + ] + }, + { + "cell_type": "markdown", + "id": "94494043-c549-4868-a67d-b2bae085f19f", + "metadata": {}, + "source": [ + "### Why does upscaling $d\\theta$ work?\n", + "\n", + "Deleting all the nodes with $\\theta$ coordinate > $\\pi$ leaves a grid where each $\\rho$ and $\\zeta$ surface has less area than it should.\n", + "By upscaling the nodes' $d\\theta$ weights we can recover this area.\n", + "It also helps to consider how this affects surface integral computations.\n", + "\n", + "After deleting the nodes, but before upscaling them we are missing perhaps $1/2$ of the $d\\theta$ weight.\n", + "So if we performed a surface integral over the grid in this state, we would be computing one of the following depending on the surface label.\n", + "$$ \\int_0^{\\pi}\\int_0^{2\\pi} d\\theta d\\zeta Q\\ + 0 \\times \\int_{\\pi}^{2\\pi}\\int_0^{2\\pi} d\\theta d\\zeta Q \\approx \\int_0^{2\\pi}\\int_0^{2\\pi} (\\frac{1}{2} d\\theta) \\ d\\zeta \\ Q$$\n", + "$$ \\int_0^{1}\\int_0^{\\pi} d\\rho d\\theta Q\\ + 0 \\times \\int_{0}^{1}\\int_{\\pi}^{2\\pi} d\\rho d\\theta Q \\approx \\int_0^{1}\\int_0^{2\\pi} d\\rho \\ (\\frac{1}{2} d\\theta) \\ Q$$\n", + "$$ \\int_0^{1}\\int_0^{2\\pi} d\\rho d\\zeta \\ Q$$\n", + "\n", + "The approximate equality follows from the assumption that $Q$ is symmetric. Clearly the integrals over $\\rho$ and $\\zeta$ surfaces would be off by some factor.\n", + "Notice that upscaling $d\\theta$ alone is enough to recover the correct integrals.\n", + "This should make sense as deleting all the nodes with $\\theta$ coordinate > $\\pi$ does not change the number of nodes over any $\\theta$ surfaces $\\implies$ integrals over $\\theta$ surfaces are not affected.\n", + "\n", + "Footnote [3]: We could also instead delete all the nodes with $\\zeta$ coordinate > $\\pi$." + ] + }, + { + "cell_type": "markdown", + "id": "24578df1-15da-410d-a734-4f194c5000a5", + "metadata": {}, + "source": [ + "## On NFP: the number of field periods\n", + "The number of field periods measures how often the stellarator geometry repeats over the toroidal coordinate.\n", + "If NFP is an integer, then all the relevant information can be determined from analyzing the chunk of the device that spans from $\\zeta = 0$ to $\\zeta = 2\\pi / \\text{NFP}$.\n", + "So we can limit the toroidal domain (the $\\zeta$ coordinates of the grid nodes) at $\\zeta = 2\\pi / \\text{NFP}$.\n", + "\n", + "In regards to the grid, this means after the grid is constructed according to the node placement and spacing rules above, we need to modify the locations of the $\\zeta$ surfaces.\n", + "We scale down the $\\zeta$ coordinate of all the nodes in a grid by a factor of NFP.\n", + "If there were $N \\ \\zeta$ surfaces before, there are still that many - we do not want to change the resolution of the grid.\n", + "The locations of the zeta surfaces are just more densely packed together within $\\zeta = 0 \\ to \\ 2\\pi / \\text{NFP}$.\n", + "\n", + "Note that we do not change the $d\\zeta$ weight assigned to each surface just because there is less space between the surfaces.\n", + "The quick way to justify this is because scaling down $d\\zeta$ would break the invariant we discussed earlier that the total volume or `grid.weights.sum()` equals 4$\\pi^2$.\n", + "\n", + "Another argument follows.\n", + "Supposing we did scale down $d\\zeta$ by NFP, then we would have surface integrals of the form $\\int_0^{2\\pi}\\int_0^{2\\pi} d\\theta (\\frac{d\\zeta}{\\text{NFP}}) Q$.\n", + "The results for these computations would depend on NFP, which is not desirable.\n", + "For example, a device which repeats its geometry twice toroidally has NFP = 2.\n", + "After $\\zeta = 2 \\pi / \\text{NFP}$, the same pattern restarts.\n", + "Of course, you could claim every device has NFP = 1 because $\\zeta$ is periodic.\n", + "After $\\zeta = 2 \\pi$, the same pattern restarts.\n", + "In this sense any device with NFP >= 2, also could be said to have NFP = 1.\n", + "If the result of the above computation depended on NFP, then the computed result would be different on the same device depending on your arbitrary choice of defining where it repeats.\n", + "\n", + "To emphasize: the columns of `grid.spacing` do not correspond to the distance between coordinates of nodes.\n", + "Instead they correspond to the differential element weights $d\\rho, d\\theta, d\\zeta$.\n", + "These differential element weights should have whatever values are needed to maintain the node volume and area invariants discussed earlier." + ] + }, + { + "cell_type": "markdown", + "id": "69a08420-a56a-4811-a8d8-45936bf6a56a", + "metadata": {}, + "source": [ + "## Duplicate nodes\n", + "\n", + "When grids are created by specifying `endpoint=True`, or inputting an array which has both 0 and $2\\pi$ as the input to the `theta` and `zeta` parameters, a grid is made with duplicate surfaces.\n", + "```python\n", + "# if theta and zeta are scalers\n", + "t = np.linspace(0, 2 * np.pi, int(theta), endpoint=endpoint)\n", + "...\n", + "z = np.linspace(0, 2 * np.pi / self.NFP, int(zeta), endpoint=endpoint)\n", + "...\n", + "# for all grids\n", + "r, t, z = np.meshgrid(r, t, z, indexing=\"ij\")\n", + "r = r.flatten()\n", + "t = t.flatten()\n", + "z = z.flatten()\n", + "nodes = np.stack([r, t, z]).T\n", + "```\n", + "\n", + "The extra value of $\\theta = 2 \\pi$ and/or $\\zeta = 2\\pi / \\text{NFP}$ in the array duplicates the $\\theta = 0$ and/or $\\zeta = 0$ surfaces.\n", + "There is a surface at $\\theta = 0 \\text{ or } 2\\pi$ with duplicity 2.\n", + "There is a surface at $\\zeta = 0 \\text{ or } 2\\pi / \\text{NFP}$ with duplicity 2.\n", + "\n", + "There are no duplicate nodes on `ConcentricGrid` or `QuadratureGrid`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "e74adbc8-f1db-417a-bbc0-11db702f3b42", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A grid with duplicate surfaces\n", + " grid nodes \n", + " 𝜌 𝜃 𝜁\n", + "[[0.000 0.000 0.000]\n", + " [0.000 3.142 0.000]\n", + " [0.000 6.283 0.000]\n", + " [1.000 0.000 0.000]\n", + " [1.000 3.142 0.000]\n", + " [1.000 6.283 0.000]\n", + " [0.000 0.000 3.142]\n", + " [0.000 3.142 3.142]\n", + " [0.000 6.283 3.142]\n", + " [1.000 0.000 3.142]\n", + " [1.000 3.142 3.142]\n", + " [1.000 6.283 3.142]\n", + " [0.000 0.000 6.283]\n", + " [0.000 3.142 6.283]\n", + " [0.000 6.283 6.283]\n", + " [1.000 0.000 6.283]\n", + " [1.000 3.142 6.283]\n", + " [1.000 6.283 6.283]]\n" + ] + } + ], + "source": [ + "lg = LinearGrid(L=1, N=1, M=1, endpoint=True)\n", + "print(\"A grid with duplicate surfaces\")\n", + "print(\" grid nodes \")\n", + "print(\" 𝜌 𝜃 𝜁\")\n", + "print(lg.nodes)" + ] + }, + { + "cell_type": "markdown", + "id": "6a1c9f9f-84c4-423b-9f57-2b555b4cf533", + "metadata": {}, + "source": [ + "### The problem with duplicate nodes\n", + "\n", + "For the above grid, all the nodes are located in two cross-sections at $\\zeta = 0 \\text{ and } \\pi$.\n", + "However the $\\zeta = 0$ surface has twice as many nodes as the $\\zeta = \\pi$ surface because of the duplicate surface at $\\zeta = 2\\pi$.\n", + "\n", + "If we wanted to sum a function which was 1 at the $\\zeta = 0$ cross section and -1 at $\\zeta = \\pi$ cross section, weighting all the nodes equally would result in an incorrect answer of $\\frac{2}{3} (1) + \\frac{1}{3} (-1) = \\frac{1}{3}$.\n", + "We would want the answer to be $0$.\n", + "By the same logic, a $\\rho$ surface integral would double count all the nodes on the $\\zeta = 0$ cross section.\n", + "\n", + "Furthermore, the $\\zeta$ spacing algorithm used when a scalar is given for the `zeta` (or `theta`) parameter discussed above would assign $d\\zeta = 2\\pi/3$ to each surface at $0, \\pi, \\text{ and } 2\\pi$.4\n", + "Since there are only two distinct surfaces in this grid, we would have liked to assign a $d\\zeta = 2\\pi / 2$ to each distinct surface $\\implies$ $d\\zeta = 2\\pi / 4$ for the two duplicate surfaces at $\\zeta = 0$.\n", + "That way the sum of the weights on the two duplicate surfaces add up to match the non-duplicate surface.\n", + "\n", + "Clearly we need to do some post-processing to correct the weights when there are duplicate surfaces.\n", + "From now on we will use the duplicate $\\zeta$ surface as an example, but the algorithm to correct the weights for a duplicate $\\theta$ surface is identical.\n", + "\n", + "Converting the previous previous paragraph into coding steps, we see that we need to:\n", + "1. Upscale the weight of all the nodes so that each distinct, non-duplicate, node has the correct weight.\n", + "2. Reduce the weight of all the duplicate nodes by dividing by the duplicity of that node. (This needs to be done in a more careful way than is suggested above).\n", + "\n", + "The first step is easier to handle before we make the `grid.nodes` mesh from the node coordinates.\n", + "The second step is handled by the `scale_weights` function.\n", + "```python\n", + "z = np.linspace(0, 2 * np.pi / self.NFP, int(zeta), endpoint=endpoint)\n", + "dz = 2 * np.pi / z.size * np.ones_like(z)\n", + "if endpoint and z.size > 1:\n", + " # increase node weight to account for duplicate node\n", + " dz *= z.size / (z.size - 1) # DOES STEP 1.\n", + " # scale_weights() will reduce endpoint (dz[0] and dz[-1]) duplicate node weight\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "9f56a8ce-e81a-40f2-82dd-7cb8a796f4bd", + "metadata": {}, + "source": [ + "### The `scale_weights` function and duplicate nodes\n", + "\n", + "This function reduces the weights of duplicate nodes.\n", + "Then, if needed, scales the weights to sum to the full volume or area." + ] + }, + { + "cell_type": "markdown", + "id": "2a37ed67-4ba2-46aa-87f9-6eb51ba8e1c6", + "metadata": {}, + "source": [ + "#### `grid.weights` $\\neq$ `grid.spacing.prod(axis=1)` when $\\exists$ duplicates\n", + "Recall the grid has two relevant attributes for node volume and areas.\n", + "The first is `weights`, which corresponds to the volume differential element $dV$ in the computational domain.\n", + "The second is `spacing`, which corresponds to the three surface differential elements $d\\rho$, $d\\theta$, and $d\\zeta$.\n", + "\n", + "When there were no duplicate nodes and symmetry off, we could think of `grid.weights` as the triple product of `grid.spacing`. In other words, the following statements were true:\n", + "```python\n", + "assert grid.weights.sum() == 4 * np.pi**2\n", + "assert grid.spacing.prod(axis=1).sum() == 4 * np.pi**2\n", + "assert np.allclose(grid.weights, grid.spacing.prod(axis=1))\n", + "```\n", + "\n", + "When there are duplicate nodes, the last two assertions above are no longer true.\n", + "Maintaining the node volume and area invariants for all three surface types simultaneously requires that `grid.weights` and `grid.spacing.prod(axis=1)` be different." + ] + }, + { + "cell_type": "markdown", + "id": "ae3b0c3a-84db-47eb-a372-229cba912042", + "metadata": {}, + "source": [ + "### How `scale_weights` affects node volume or `grid.weights`\n", + "\n", + "This process is relatively simple.\n", + "1. We scan through the nodes looking for duplicates.\n", + "```python\n", + "_, inverse, counts = np.unique(\n", + " nodes, axis=0, return_inverse=True, return_counts=True\n", + ")\n", + "duplicates = np.tile(np.atleast_2d(counts[inverse]).T, 3)\n", + "```\n", + "2. Then we divide the duplicate nodes by their duplicity\n", + "```python\n", + "temp_spacing /= duplicates ** (1 / 3)\n", + "# scale weights sum to full volume\n", + "temp_spacing *= (4 * np.pi**2 / temp_spacing.prod(axis=1).sum()) ** (1 / 3)\n", + "self._weights = temp_spacing.prod(axis=1)\n", + "```\n", + "The power factor of $1/3$ is there because we want to scale down the final node weight, which is the product of the three columns, by the number of duplicates.\n", + "Dividing each column by the cube root of this factor does the job.\n", + "\n", + "Scaling down the duplicate nodes so that they have the same node volume (`grid.weights`) is relatively simple because we can ignore whether the dimensions of the node $d\\rho$, $d\\theta$, and $d\\zeta$ are correct.\n", + "All we care about is whether the final product is the correct value.\n", + "If there is a location on the grid with two nodes, we just half the volume of each of those nodes so that their total volume is the same as a non-duplicate node." + ] + }, + { + "cell_type": "markdown", + "id": "460f26a1-bc92-45c4-80d9-73f4c29f6e4e", + "metadata": {}, + "source": [ + "### How `scale_weights` affects node areas or `grid.spacing`\n", + "\n", + "This process is more complicated because we need to make sure the node has the correct area for all three types of surfaces simultaneously.\n", + "That is, we need correct values for $d\\theta \\times d\\zeta$, $d\\zeta \\times d\\rho$, and $d\\rho \\times d\\theta$.\n", + "\n", + "If there is a node with duplicity $N$, this node has $N$ times the area (and volume) it should have.\n", + "If we compute any of these integrals on the grid in this state:\n", + "$$\\int_0^{2\\pi}\\int_0^{2\\pi} d\\theta d\\zeta = \\sum_{i} d\\theta \\times d\\zeta \\neq 4\\pi^2$$\n", + "$$\\int_0^{1}\\int_0^{2\\pi} d\\rho d\\zeta = \\sum_{i} d\\rho \\times d\\zeta \\neq 2\\pi$$\n", + "$$\\int_0^{1}\\int_0^{2\\pi} d\\rho d\\theta = \\sum_{i} d\\rho \\times d\\theta \\neq 2\\pi$$\n", + "There exists $N$ indices which correspond to the same duplicated node.\n", + "This node would contribute $N$ times the area product it should.\n", + "\n", + "- To get the correct $\\rho$ surface area we should scale this node's $d\\theta \\times d\\zeta$ by $1/N$. This can be done by multiplying $d\\theta$ and $d\\zeta$ each by $(\\frac{1}{N})^{\\frac{1}{2}}$. Changing $d\\rho$ has no effect on this area.\n", + "- To get the correct $\\theta$ surface area we should scale this node's $d\\zeta \\times d\\rho$ by $1/N$. This can be done by multiplying $d\\zeta$ and $d\\rho$ each by $(\\frac{1}{N})^{\\frac{1}{2}}$. Changing $d\\theta$ has no effect on this area.\n", + "- To get the correct $\\zeta$ surface area we should scale this node's $d\\rho \\times d\\theta$ by $1/N$. This can be done by multiplying $d\\rho$ and $d\\theta$ each by $(\\frac{1}{N})^{\\frac{1}{2}}$. Changing $d\\zeta$ has no effect on this area.\n", + "\n", + "Hence, we can get the correct areas for the three surfaces simultaneously by dividing $d\\rho$ and $d\\theta$ and $d\\zeta$ by the square root of the duplicity. The extra multiplication by $(\\frac{1}{N})^{\\frac{1}{2}}$ to the other differential element is ignorable because any area only involves the product of two differential elements at a time.\n", + "```python\n", + "# The reduction of weight on duplicate nodes should be accounted for\n", + "# by the 2 columns of spacing which span the surface.\n", + "self._spacing /= duplicates ** (1 / 2)\n", + "```\n", + "\n", + "Now, when we compute any of these integrals\n", + "$$\\int_0^{2\\pi}\\int_0^{2\\pi} d\\theta d\\zeta = \\sum_{i} d\\theta \\times d\\zeta = 4\\pi^2$$\n", + "$$\\int_0^{1}\\int_0^{2\\pi} d\\rho d\\zeta = \\sum_{i} d\\rho \\times d\\zeta = 2\\pi$$\n", + "$$\\int_0^{1}\\int_0^{2\\pi} d\\rho d\\theta = \\sum_{i} d\\rho \\times d\\theta = 2\\pi$$\n", + "and we hit an index which corresponds to that of a node with duplicity N, the area product of that index will be scaled down by $1/N$.\n", + "There will be $N$ indices corresponding to this node so the total area the node contributes is the same as any non-duplicate node: $N \\times 1/N \\times$ area of non-duplicate node." + ] + }, + { + "cell_type": "markdown", + "id": "6aa6d9e6-3452-4168-b5b3-183b6780fa56", + "metadata": {}, + "source": [ + "### Why not just...\n", + "\n", + "> - scale down only $d\\rho$ by $\\frac{1}{N}$ on the duplicate node when it lies on a $\\rho$ surface of duplicity $N$?\n", + "> - scale down only $d\\theta$ by $\\frac{1}{N}$ on the duplicate node when it lies on a $\\theta$ surface of duplicity $N$?\n", + "> - scale down only $d\\zeta$ by $\\frac{1}{N}$ on the duplicate node when it lies on a $\\zeta$ surface of duplicity $N$?\n", + "\n", + "> That way, the area product at each duplicate node index is still downscaled by a factor of $\\frac{1}{N}$.\n", + "And the correct node \"lengths\" are preserved too.\n", + "What am I missing?\n", + "\n", + "That method would not calculate the area of the duplicate surface correctly.\n", + "It accounts for the thickness of the duplicate surface correctly, but it doesn't account for the extra nodes on the duplicate surface.\n", + "\n", + "For example consider a grid with a $\\zeta = 0$ surface of duplicity $N$.\n", + "If we apply the technique of just scaling down $d\\zeta$ for the nodes on this surface by $\\frac{1}{N}$, then as discussed above, the $\\rho$ and $\\theta$ surface areas on all duplicate nodes will be correct: $N \\times (d\\theta \\times \\frac{d\\zeta}{N})$ or $N \\times (d\\rho \\times \\frac{d\\zeta}{N})$, respectively.\n", + "\n", + "However, the $\\zeta$ surface area for the duplicate surface will not be correct.\n", + "This is because there are $N$ times as many nodes on this surface, so the sum is over $N$ times as many indices.\n", + "Observe that, on a surface without duplicates, with a total of $K$ nodes on that surface we have\n", + "$$\\int_0^{1}\\int_0^{2\\pi} d\\rho d\\theta = \\sum_{i=1}^{i=K} d\\rho \\times d\\theta = 2\\pi$$\n", + "If this surface had duplicity $N$, the sum would have run over $N$ times as many indices.\n", + "$$\\sum_{i=1}^{i=K N} d\\rho \\times d\\theta = N \\sum_{i=1}^{i=K} d\\rho \\times d\\theta = N \\times 2\\pi$$\n", + "\n", + "To obtain the correct result we need each node on this $\\zeta$ surface of duplicity $N$ to have a $\\zeta$ surface area of $\\frac{1}{N} (d\\rho \\times d\\theta)$.\n", + "This requirement is built into the previous algorithm where all the differential elements of duplicate nodes were scaled by $\\frac{1}{N^{1/2}}$.\n", + "$$\\sum_{i=1}^{i=K N} (\\frac{1}{N^{1/2}} d\\rho) \\times (\\frac{1}{N^{1/2}} d\\theta) = N \\sum_{i=1}^{i=K} \\frac{1}{N} d\\rho \\times d\\theta = 2\\pi$$" + ] + }, + { + "cell_type": "markdown", + "id": "7fb946b9-45f2-4691-9125-ead0a831adca", + "metadata": {}, + "source": [ + "### Verdict\n", + "When there is a node of duplicity $N$, we need to reduce the area product of each pair of differential elements ($d\\theta \\times d\\zeta$, $d\\zeta \\times d\\rho$, and $d\\rho \\times d\\theta$) by $\\frac{1}{N}$.\n", + "The only way to do this is by reducing each differential element by $\\frac{1}{N^{1/2}}$." + ] + }, + { + "cell_type": "markdown", + "id": "6b1b7f9f-8f89-4247-8b77-fd4e15ad7532", + "metadata": {}, + "source": [ + "### Recap and intuition for duplicate nodes\n", + "\n", + "Recall when there is a duplicate node we need to do two steps:\n", + "> 1. Upscale the weight of all the nodes so that each distinct, non-duplicate, node has the correct weight.\n", + "> 2. Reduce the weight of all the duplicate nodes by dividing by the duplicity of that node.\n", + "\n", + "Weight may refer to volume, area, or length.\n", + "\n", + "To correct the volume weights when there is a duplicate node:\n", + "```python\n", + "temp_spacing = np.copy(self.spacing)\n", + "temp_spacing /= duplicates ** (1 / 3) # STEP 2\n", + "temp_spacing *= (4 * np.pi**2 / temp_spacing.prod(axis=1).sum()) ** (1 / 3) # STEP 1\n", + "self._volumes = temp_spacing.prod(axis=1)\n", + "```\n", + "\n", + "To correct the area weights when there is a duplicate node:\n", + "```python\n", + "self._areas = np.copy(self.spacing)\n", + " # STEP 1:\n", + " # for each surface label\n", + " # if spacing was assigned as max_surface_val / number of surfaces, then\n", + " # scale the differential element of the same surface label (e.g. dzeta) by:\n", + " # number of surfaces / number of unique surfaces\n", + " # done in LinearGrid construction\n", + "self._areas /= duplicates ** (1 / 2) # STEP 2\n", + "```\n", + "\n", + "To correct the length weights when there is a duplicate node:\n", + "```python\n", + "self._lengths = np.copy(self.spacing)\n", + " # STEP 1:\n", + " # for each surface label\n", + " # if spacing was assigned as max_surface_val / number of surfaces, then\n", + " # scale the differential element of the same surface label (e.g. dzeta) by:\n", + " # number of nodes per line integral / number of unique nodes per line integral\n", + " # which equals surfaces / number of unique surfaces for LinearGrid\n", + " # done in LinearGrid construction\n", + "self._lengths /= duplicates ** (1 / 1) # STEP 2\n", + "```\n", + "\n", + "Three attributes are required when there are duplicate nodes.\n", + "\n", + "Currently in `grid.py`, the\n", + "- `_volumes` attribute is `_weights`,\n", + "- `_areas` attribute is `_spacing`,\n", + "- There is no `_lengths` attribute. Because the column from the areas grid is used, line integrals overweight duplicate nodes by the square root of the duplicity." + ] + }, + { + "cell_type": "markdown", + "id": "23dc9427-5308-40f9-90f7-123a1bf5bfbd", + "metadata": {}, + "source": [ + "### Duplicate nodes on custom user-defined grids\n", + "\n", + "At the start of this section it was mentioned that\n", + "> The first step is easier to handle before we make the `grid.nodes` mesh from the node coordinates.\n", + "The second step is handled by the `scale_weights` function.\n", + "\n", + "Before the `grid.nodes` mesh is created on `LinearGrid` we have access to three arrays which specify the values of all the surfaces: `rho`, `theta`, and `zeta`.\n", + "If there is a duplicate surface, we can just check for a repeated value in these arrays.\n", + "This makes it easy to find the correct upscale factor of (number of surfaces / number of unique surfaces) for this surface's spacing.\n", + "\n", + "For custom user-defined grids, the user provides the `grid.nodes` mesh directly.\n", + "Because `grid.nodes` is just a list of coordinates, it is hard to determine what surface a duplicate node belongs to.\n", + "Any point in space lies on all three surfaces.\n", + "\n", + "Because of this, the `scale_weights` function includes a line of code to attempt to address step 1 for areas:\n", + "```python\n", + "# scale areas sum to full area\n", + "# The following operation is not a general solution to return the weight\n", + "# removed from the duplicate nodes back to the unique nodes.\n", + "# For this reason, duplicates should typically be deleted rather than rescaled.\n", + "# Note we multiply each column by duplicates^(1/6) to account for the extra\n", + "# division by duplicates^(1/2) in one of the columns above.\n", + "self._spacing *= (\n", + " 4 * np.pi**2 / (self.spacing * duplicates ** (1 / 6)).prod(axis=1).sum()\n", + ") ** (1 / 3)\n", + "```\n", + "For grids without duplicates and grids with duplicates which have already done the upscaling mentioned in the first step (such as `LinearGrid`), this line of code will have no effect.\n", + "It should only affect custom grids with duplicates.\n", + "As the comment mentions, this line does not do its job ideally because it scales up the volumes rather than each of the areas.\n", + "There is a method to upscale the areas correctly after the node mesh is created, but I do not think there is a valid use case that justifies developing it.\n", + "The main use case for duplicate nodes on `LinearGrid` is to add one at the endpoint of the periodic domains to make closed intervals for plotting purposes." + ] + }, + { + "cell_type": "markdown", + "id": "c2cf249f-b4ee-405a-8666-01e404a1992f", + "metadata": {}, + "source": [ + "### `LinearGrid` with `endpoint` duplicate at $\\theta = 2\\pi$ and `symmetry`\n", + "\n", + "If this is the case, the duplicate surface at $\\theta = 2\\pi$ will be deleted by symmetry,\n", + "while the remaining surface at $\\theta = 0$ will remain.\n", + "As this surface will no longer be a duplicate, we need to prevent both step 1 and step 2 from occurring.\n", + "\n", + "Step 2 is prevented by calling `enforce_symmetry` prior to `scale_weights`, so that the duplicate node is deleted before it is detected and scaled down.\n", + "Step 1 is prevented with an additional conditional guard that determines whether to upscale $d\\theta$.\n", + "```python\n", + "if (endpoint and not self.sym) and t.size > 1:\n", + " # increase node weight to account for duplicate node\n", + " dt *= t.size / (t.size - 1)\n", + " # scale_weights() will reduce endpoint (dt[0] and dt[-1])\n", + " # duplicate node weight\n", + "```" + ] + } + ], + "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.16" + }, + "toc-autonumbering": true, + "toc-showcode": true, + "toc-showmarkdowntxt": false + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/backend.ipynb b/docs/dev_guide/notebooks/backend.ipynb new file mode 100644 index 0000000000..1303924440 --- /dev/null +++ b/docs/dev_guide/notebooks/backend.ipynb @@ -0,0 +1,35 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "eac59858-706b-47f0-ab7a-04e2a457ade9", + "metadata": { + "tags": [] + }, + "source": [ + "# `backend.py`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/basis.ipynb b/docs/dev_guide/notebooks/basis.ipynb new file mode 100644 index 0000000000..fb1abdc2c2 --- /dev/null +++ b/docs/dev_guide/notebooks/basis.ipynb @@ -0,0 +1,35 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2e2ddee9-0fb9-438e-b078-c4c5e000d875", + "metadata": { + "tags": [] + }, + "source": [ + "# `basis.py`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/coils.ipynb b/docs/dev_guide/notebooks/coils.ipynb new file mode 100644 index 0000000000..ee4059d4f7 --- /dev/null +++ b/docs/dev_guide/notebooks/coils.ipynb @@ -0,0 +1,221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "17782242-c991-484d-bb46-811952ee9c38", + "metadata": {}, + "source": [ + "# `coils.py`" + ] + }, + { + "cell_type": "markdown", + "id": "febe173a-38c4-44ec-ba2f-6d556b22dd50", + "metadata": { + "tags": [] + }, + "source": [ + "## Introduction" + ] + }, + { + "cell_type": "markdown", + "id": "4848dc72-9eaf-41cf-9937-aa937287b901", + "metadata": { + "tags": [] + }, + "source": [ + "In DESC, quantities of interest (such as force error `F`, magnetic field strength `|B|` and its vector components) are computed from `Equilibrium` objects through the use of `compute_XXX` functions. The `Equilibrium` object has a `eq.compute(name=\"NAME OF QUANTITY TO COMPUTE\",grid=Grid)` method which takes in the string `name` of the quantity to compute from the `Equilibrium`, and a `Grid` of coordinates $(\\rho,\\theta,\\zeta)$ to evaluate the quantity at. This method then uses the `name` to find which `compute_XXX` function is needed to call in order to calculate that quantity.\n", + "\n", + "These `compute_XXX` functions live inside of the `desc/compute` folder, and inside that folder the `data_index.py` file contains the list of all available quantities that `name` could specify to be computed, as well as information on that quantity and which `compute_XXX` function should be called in order to compute it.\n", + "\n", + "The `compute_XXX` functions have function signatures that take in as arguments the necessary variables from `{R_lmn, Z_lmn, L_lmn, i_l, p_l,Psi}` required to calculate the quantity, as well as the `Transform` and/or `Profile` objects needed to evaluate those variables (which are spectral coefficients, except for `Psi`) at the points in real space specified by the desired `Grid` object (which is contained in the `Transform` objects passed).\n", + "\n", + "\n", + "An example compute function from `_field.py` is shown here for computing the magnetic field magnitude:\n", + "```python\n", + "def compute_magnetic_field_magnitude(\n", + " R_lmn,\n", + " Z_lmn,\n", + " L_lmn,\n", + " i_l,\n", + " Psi,\n", + " R_transform,\n", + " Z_transform,\n", + " L_transform,\n", + " iota,\n", + " data=None,\n", + " **kwargs,\n", + "):\n", + "```\n", + "The first 3 arguments `R_lmn,Z_lmn,L_lmn` are the Fourier-Zernike spectral coefficients of the toroidal coordinates of the flux surfaces `R,Z`, and of the poloidal stream function $\\lambda$. `i_l` is the coefficients of the rotational transform profile (typically a power series in `rho`). `Psi` is the enclosed toroidal flux.\n", + "\n", + "The `_transform` arguments are `Transform` objects which transform the spectral coefficients to their values in real-space. `iota` is a `Profile` object which transforms `i_l` into its values in real-space.\n", + "The `data` argument is an optional dictionary. The compute functions store the quantities they calculate in a `data` dictionary and return it, and if `data` is passed in, the quantities this function computes will be added to this dictionary. This way, a compute function can add on to the quantities already calculated by previous compute functions, or it can use other compute functions to calculate preliminary quantities to avoid duplicating code. As an example, in the `compute_magnetic_field_magnitude` function, the contravariant components of the magnetic field $B^i$ are required, along with the metric coefficients $g_ij$. To calculate these, the function calls:\n", + "\n", + "```python\n", + "data = compute_contravariant_magnetic_field(\n", + " R_lmn,\n", + " Z_lmn,\n", + " L_lmn,\n", + " i_l,\n", + " Psi,\n", + " R_transform,\n", + " Z_transform,\n", + " L_transform,\n", + " iota,\n", + " data=data,\n", + ")\n", + "data = compute_covariant_metric_coefficients(\n", + " R_lmn, Z_lmn, R_transform, Z_transform, data=data\n", + ")\n", + "```\n", + "\n", + "in order to populate `data` with these necessary preliminary quantities.\n", + "\n", + "\n", + "- talk about what the data arg is and how it is used\n", + "- maybe include example of how to make your own (let's say for a stupid thing like B_theta * B_zeta)\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "f4737eaf-3f27-425a-a50d-2e439e2bdb91", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "## Calculating Quantities" + ] + }, + { + "cell_type": "markdown", + "id": "d613b5af-21b0-4a4c-be45-181b81ab0c0b", + "metadata": {}, + "source": [ + "Inside the compute function, every quantity is stored inside the `data` dictionary under the key of the name of the quantity. \n", + "As an example, `data['|B|']` contains the magnetic field magnitude.\n", + "This quantity is stored as a JAX array of size `(num_nodes,)` (if the quantity is NOT a vector), or `(num_nodes,3)` (if the quantity is a vector, i.e. `data['B']`, which contains all three components $[B_R, B_{\\phi},B_{Z}]$ of $B$ at each node) (`num_nodes` is the number of nodes in the `Grid` object that the quantity was computed on. Can be accessed by `grid.num_nodes`). \n", + "This array is flattened, so if the grid used has 10 equispaced gridpoints in $(\\rho,\\theta,\\zeta)$, the grid will have $10^3 = 1000$ nodes, and any quantity calculated on that grid will be returned as an array of size `(num_nodes,)` if not a vector and `(num_nodes,3)` if a vector quantity.\n", + "### Scalar Algebra\n", + "Storing the quantities in arrays like this enables for easy computation using these quantities. For example, if one wants to calculate the magnitude of the pressure gradient $|\\nabla p(\\rho)| = \\sqrt{p'(\\rho)^2}|\\nabla\\rho|$, one simply [writes out the expression](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_equil.py#L114) after calling the necessary compute functions:\n", + "\n", + "```python\n", + "data[\"|grad(p)|\"] = jnp.sqrt(data[\"p_r\"] ** 2) * data[\"|grad(rho)|\"]\n", + "```\n", + "\n", + "### Vector Algebra\n", + "If calculating a quantity which involves vector algebra, the format of these arrays makes it simple to write out as well. As an example, if calculating the contravariant radial basis vector $\\mathbf{e}^{\\rho} = \\frac{\\mathbf{e}^{\\theta} \\times \\mathbf{e}^{\\zeta}}{\\sqrt{g}}$, one [writes](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_core.py#L426):\n", + "```python\n", + "data[\"e^rho\"] = (cross(data[\"e_theta\"], data[\"e_zeta\"]).T / data[\"sqrt(g)\"]).T\n", + "```\n", + "Note here that once the quantities are crossed, they are transposed. This is done to ensure that the result retains the desired shape of `(num_nodes,3)`.\n", + "\n", + "### Be Mindful of Shapes\n", + "\n", + "It is important to keep in mind the shapes of the quantities being manipulated to ensure the desired operation is carried out. As another example, the gradient of the magnetic toroidal flux $\\nabla \\psi = \\frac{d\\psi}{d\\rho}\\nabla \\rho$ is [calculated as](https://github.com/PlasmaControl/DESC/blob/94d7e43542613b1c901fcd655502312f3e567c26/desc/compute/_core.py#L701):\n", + "```python\n", + "data[\"grad(psi)\"] = (data[\"psi_r\"] * data[\"e^rho\"].T).T\n", + "```\n", + "The desired operation here is to multiply `data[\"psi_r\"]`, which is a scalar quantity at each grid point and so is of shape `(num_nodes,)` with `data[\"e^rho\"]`, a vector quantity and so is of shape `(num_nodes,3)`. \n", + "We want the result to be of shape `(num_nodes,3)`. \n", + "In order to do so, we first must transpose the vector quantity to be shape `(3,num_nodes)`, so that when multiplied together with the scalar quantity of shape `(num_nodes,3)`, the result is broadcast to an array of shape `(3,num_nodes)`. \n", + "If the transpose did not occur, the two shapes `(num_nodes,)` and `(num_nodes,3)` would be incompatible with eachother.\n", + "The second transpose after the multiplication is to ensure that the result is in the shape `(num_nodes,3)`, as is the convention expected in the code." + ] + }, + { + "cell_type": "markdown", + "id": "8c765ae4-ea52-4360-92a4-e91126efc51f", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "## What `check_derivs()` does" + ] + }, + { + "cell_type": "markdown", + "id": "9f2c39bb-2bc3-413c-b2c3-428619da1faa", + "metadata": {}, + "source": [ + "Basically, this function ensures that the transforms passed to the compute function have the necessary derivatives of $R,Z,L,p,\\iota$ to calculate the quantity contained in the if statement. \n", + "If yes, it returns `True` and the quantity in the logival is computed. If not, it returns `False` and that quantitiy is not calculated. \n", + "This allows us to call a function to get a specific quantity which may not need high order derivatives, and avoid needing to compute those derivatives anyways just to have the function call not throw an error that the necessary derivatives do not exist for a quantitiy we are not asking for but which needs higher order derivatives to compute" + ] + }, + { + "cell_type": "markdown", + "id": "21ec3f84-f929-4757-a5de-47824e50acd9", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "## `__init__.py`" + ] + }, + { + "cell_type": "markdown", + "id": "4efca99b-2587-4fb8-bd26-20d1299a365e", + "metadata": {}, + "source": [ + "`arg_order` is defined in this file. This tuple is used in other parts of the code to determine how to parse the state vector `x` into the various arguments that make it up, and also for making the derivatives of functions of these arguments, such as inside of `_set_derivatives` method of `_Objective` in `objective_funs.py`." + ] + }, + { + "cell_type": "markdown", + "id": "d96ae667-54e3-4434-aa5f-11a4e00b250b", + "metadata": { + "tags": [] + }, + "source": [ + "## `compute/utils.py`" + ] + }, + { + "cell_type": "markdown", + "id": "e304bafa-cd0a-4438-b181-037ba316aee3", + "metadata": {}, + "source": [ + " - dot\n", + " - cross\n", + " custom vector algebra fxns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a1f7820-a65b-4fb8-8ccc-1a61f1c50e9a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/compute_notebook_2.ipynb b/docs/dev_guide/notebooks/compute_notebook_2.ipynb new file mode 100644 index 0000000000..6bc5a08e93 --- /dev/null +++ b/docs/dev_guide/notebooks/compute_notebook_2.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5185c760-63c6-42b8-8a5a-ce6eaebdbd52", + "metadata": { + "tags": [], + "toc-hr-collapsed": true + }, + "source": [ + "# compute" + ] + }, + { + "cell_type": "markdown", + "id": "febe173a-38c4-44ec-ba2f-6d556b22dd50", + "metadata": { + "tags": [] + }, + "source": [ + "## Introduction" + ] + }, + { + "cell_type": "markdown", + "id": "4848dc72-9eaf-41cf-9937-aa937287b901", + "metadata": { + "tags": [] + }, + "source": [ + "In DESC, quantities of interest (such as force error `F`, magnetic field strength `|B|` and its vector components) are computed from `Equilibrium` objects through the use of `compute_XXX` functions. The `Equilibrium` object has a `eq.compute(name=\"NAME OF QUANTITY TO COMPUTE\",grid=Grid)` method which takes in the string `name` of the quantity to compute from the `Equilibrium`, and a `Grid` of coordinates $(\\rho,\\theta,\\zeta)$ to evaluate the quantity at. This method then uses the `name` to find which `compute_XXX` function is needed to call in order to calculate that quantity.\n", + "\n", + "These `compute_XXX` functions live inside of the `desc/compute` folder, and inside that folder the `data_index.py` file contains the list of all available quantities that `name` could specify to be computed, as well as information on that quantity and which `compute_XXX` function should be called in order to compute it.\n", + "\n", + "The `compute_XXX` functions have function signatures that take in as arguments the necessary variables from `{R_lmn, Z_lmn, L_lmn, i_l, c_l, p_l,Psi}` required to calculate the quantity contained in a dict argument named `params`, as well as the `Transform` objects (in the `transforms` dict argument) and/or `Profile` objects (in the `profiles` dict argument) needed to evaluate those variables in `params` (which are spectral coefficients, except for `Psi`) at the points in real space specified by the desired `Grid` object (which is contained in the `Transform` objects passed).\n", + "\n", + "\n", + "An example compute function from `_field.py` is shown here for computing the magnetic field magnitude:\n", + "```python\n", + "def compute_magnetic_field_magnitude(\n", + " params,\n", + " transforms,\n", + " profiles,\n", + " data=None,\n", + " **kwargs,\n", + "):\n", + "```\n", + "Every compute function in DESC has the same function signature:\n", + "\n", + " - `params` is a dict of the basic parameters needed to compute data, i.e. `{R_lmn, Z_lmn, L_lmn, i_l, c_l p_l,Psi}`\n", + " - The possible params are: \n", + " - `R_lmn Z_lmn, L_lmn` the Fourier-Zernike spectral coeffiiencts describing the toroidal coordinates R and Z of the flux surfaces and the poloidal stream function $\\lambda$ (`L_lmn`).\n", + " - `i_l, c_l, p_l` the parameters (either spectral coefficients for a `PowerSeriesProfile` or spline values for a `SplineProfile` ) for the profiles of rotational transform (`i_l`), net enclosed toroidal current (`c_l`) and pressure (`p_l`). Note that only one of `i_l,c_l` are needed, and if both are passed the rotational transform `i_l` will be used.\n", + " - `Psi` is the total enclosed toroidal flux, a scalar, in Wb.\n", + " - `transforms` is a dict of the transforms (`Transform` objects) needed to transform the spectral coefficients from `params` to their values in real space.\n", + " - `profiles` is a dict of the profiles (`Profile` objects) needed to evaluate the radial profiles of pressure, rotational transform and net enclosed toroidal current\n", + " - `data` argument is an optional dictionary. The compute functions store the quantities they calculate in a `data` dictionary and return it, and if `data` is passed in, the quantities this function computes will be added to this dictionary. This way, a compute function can add on to the quantities already calculated by previous compute functions, or it can use other compute functions to calculate preliminary quantities to avoid duplicating code.\n", + "\n", + "The first 3 arguments `R_lmn,Z_lmn,L_lmn` are the Fourier-Zernike spectral coefficients of the toroidal coordinates of the flux surfaces `R,Z`, and of the poloidal stream function $\\lambda$. `i_l` is the coefficients of the rotational transform profile (typically a power series in `rho`). `Psi` is the enclosed toroidal flux.\n", + "\n", + "The `_transform` arguments are `Transform` objects which transform the spectral coefficients to their values in real-space. `iota` is a `Profile` object which transforms `i_l` into its values in real-space.\n", + "As an example, in the `compute_magnetic_field_magnitude` function, the contravariant components of the magnetic field $B^i$ are required, along with the metric coefficients $g_ij$. To calculate these, the function calls:\n", + "\n", + "```python\n", + " data = compute_contravariant_magnetic_field(\n", + " params,\n", + " transforms,\n", + " profiles,\n", + " data=data,\n", + " **kwargs,\n", + " )\n", + " data = compute_covariant_metric_coefficients(\n", + " params,\n", + " transforms,\n", + " profiles,\n", + " data=data,\n", + " **kwargs,\n", + " )\n", + "```\n", + "\n", + "in order to populate `data` with these necessary preliminary quantities.\n", + "\n", + "\n", + "- maybe include example of how to make your own (let's say for a simple thing like B_theta * B_zeta)\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "f4737eaf-3f27-425a-a50d-2e439e2bdb91", + "metadata": { + "tags": [] + }, + "source": [ + "## Calculating Quantities" + ] + }, + { + "cell_type": "markdown", + "id": "d613b5af-21b0-4a4c-be45-181b81ab0c0b", + "metadata": {}, + "source": [ + "Inside the compute function, every quantity is stored inside the `data` dictionary under the key of the name of the quantity. \n", + "As an example, `data['|B|']` contains the magnetic field magnitude.\n", + "This quantity is stored as a JAX array of size `(num_nodes,)` (if the quantity is NOT a vector), or `(num_nodes,3)` (if the quantity is a vector, i.e. `data['B']`, which contains all three components $[B_R, B_{\\phi},B_{Z}]$ of $B$ at each node) (`num_nodes` is the number of nodes in the `Grid` object that the quantity was computed on. Can be accessed by `grid.num_nodes`). \n", + "This array is flattened, so if the grid used has 10 equispaced gridpoints in $(\\rho,\\theta,\\zeta)$, the grid will have $10^3 = 1000$ nodes, and any quantity calculated on that grid will be returned as an array of size `(num_nodes,)` if not a vector and `(num_nodes,3)` if a vector quantity.\n", + "### Scalar Algebra\n", + "Storing the quantities in arrays like this enables for easy computation using these quantities. For example, if one wants to calculate the magnitude of the pressure gradient $|\\nabla p(\\rho)| = \\sqrt{p'(\\rho)^2}|\\nabla\\rho|$, one simply [writes out the expression](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_equil.py#L114) after calling the necessary compute functions:\n", + "\n", + "```python\n", + "data[\"|grad(p)|\"] = jnp.sqrt(data[\"p_r\"] ** 2) * data[\"|grad(rho)|\"]\n", + "```\n", + "\n", + "### Vector Algebra\n", + "If calculating a quantity which involves vector algebra, the format of these arrays makes it simple to write out as well. As an example, if calculating the contravariant radial basis vector $\\mathbf{e}^{\\rho} = \\frac{\\mathbf{e}^{\\theta} \\times \\mathbf{e}^{\\zeta}}{\\sqrt{g}}$, one [writes](https://github.com/PlasmaControl/DESC/blob/6d03cb015701b27d651bf804d36032c35119c536/desc/compute/_core.py#L426):\n", + "```python\n", + "data[\"e^rho\"] = (cross(data[\"e_theta\"], data[\"e_zeta\"]).T / data[\"sqrt(g)\"]).T\n", + "```\n", + "Note here that once the quantities are crossed, they are transposed. This is done to ensure that the result retains the desired shape of `(num_nodes,3)`.\n", + "\n", + "### Be Mindful of Shapes\n", + "\n", + "It is important to keep in mind the shapes of the quantities being manipulated to ensure the desired operation is carried out. As another example, the gradient of the magnetic toroidal flux $\\nabla \\psi = \\frac{d\\psi}{d\\rho}\\nabla \\rho$ is [calculated as](https://github.com/PlasmaControl/DESC/blob/94d7e43542613b1c901fcd655502312f3e567c26/desc/compute/_core.py#L701):\n", + "```python\n", + "data[\"grad(psi)\"] = (data[\"psi_r\"] * data[\"e^rho\"].T).T\n", + "```\n", + "The desired operation here is to multiply `data[\"psi_r\"]`, which is a scalar quantity at each grid point and so is of shape `(num_nodes,)` with `data[\"e^rho\"]`, a vector quantity and so is of shape `(num_nodes,3)`. \n", + "We want the result to be of shape `(num_nodes,3)`. \n", + "In order to do so, we first must transpose the vector quantity to be shape `(3,num_nodes)`, so that when multiplied together with the scalar quantity of shape `(num_nodes,3)`, the result is broadcast to an array of shape `(3,num_nodes)`. \n", + "If the transpose did not occur, the two shapes `(num_nodes,)` and `(num_nodes,3)` would be incompatible with eachother.\n", + "The second transpose after the multiplication is to ensure that the result is in the shape `(num_nodes,3)`, as is the convention expected in the code." + ] + }, + { + "cell_type": "markdown", + "id": "8c765ae4-ea52-4360-92a4-e91126efc51f", + "metadata": { + "tags": [] + }, + "source": [ + "## What `check_derivs()` does" + ] + }, + { + "cell_type": "markdown", + "id": "9f2c39bb-2bc3-413c-b2c3-428619da1faa", + "metadata": {}, + "source": [ + "Basically, this function ensures that the transforms passed to the compute function have the necessary derivatives of $R,Z,L,p,\\iota$ to calculate the quantity contained in the if statement. \n", + "If yes, it returns `True` and the quantity in the logival is computed. If not, it returns `False` and that quantitiy is not calculated. \n", + "This allows us to call a function to get a specific quantity which may not need high order derivatives, and avoid needing to compute those derivatives anyways just to have the function call not throw an error that the necessary derivatives do not exist for a quantitiy we are not asking for but which needs higher order derivatives to compute" + ] + }, + { + "cell_type": "markdown", + "id": "21ec3f84-f929-4757-a5de-47824e50acd9", + "metadata": { + "tags": [] + }, + "source": [ + "## `__init__.py`" + ] + }, + { + "cell_type": "markdown", + "id": "4efca99b-2587-4fb8-bd26-20d1299a365e", + "metadata": {}, + "source": [ + "`arg_order` is defined in this file. This tuple is used in other parts of the code to determine how to parse the state vector `x` into the various arguments that make it up, and also for making the derivatives of functions of these arguments, such as inside of `_set_derivatives` method of `_Objective` in `objective_funs.py`." + ] + }, + { + "cell_type": "markdown", + "id": "e06c0cc5-cc42-4b08-a7b0-87b91bb62970", + "metadata": {}, + "source": [ + "why does arg_order exist again? It is so we can check if things have the necessary arguments?\n", + "\n", + "we need canonical ordering of things so when we combine all the args into x and all the constraints into A everything lines up correctly. We also use it in some places for a shorthand of all the args that could be used by any objective, but i think in those cases we only ever need to know about args that are taken by the objectives at hand, so we could just use that" + ] + }, + { + "cell_type": "markdown", + "id": "d96ae667-54e3-4434-aa5f-11a4e00b250b", + "metadata": { + "tags": [] + }, + "source": [ + "## `compute/utils.py`" + ] + }, + { + "cell_type": "markdown", + "id": "e304bafa-cd0a-4438-b181-037ba316aee3", + "metadata": {}, + "source": [ + " - dot\n", + " - cross\n", + " custom vector algebra fxns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a1f7820-a65b-4fb8-8ccc-1a61f1c50e9a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/configuration.ipynb b/docs/dev_guide/notebooks/configuration.ipynb new file mode 100644 index 0000000000..30de42c414 --- /dev/null +++ b/docs/dev_guide/notebooks/configuration.ipynb @@ -0,0 +1,41 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8afe8fe-6595-480a-9a61-2834d3707345", + "metadata": {}, + "source": [ + "# `configuration.py`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c774a214-f846-4f26-a851-86013eb702fa", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/derivatives.ipynb b/docs/dev_guide/notebooks/derivatives.ipynb new file mode 100644 index 0000000000..ea63f3cbf9 --- /dev/null +++ b/docs/dev_guide/notebooks/derivatives.ipynb @@ -0,0 +1,41 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "954a4637-4695-481d-b382-e6fba50e9bc8", + "metadata": {}, + "source": [ + "# `derivatives.py`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c774a214-f846-4f26-a851-86013eb702fa", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/equilibrium.ipynb b/docs/dev_guide/notebooks/equilibrium.ipynb new file mode 100644 index 0000000000..2d458d0635 --- /dev/null +++ b/docs/dev_guide/notebooks/equilibrium.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7084535b-789c-4ea5-9c95-87d54255669a", + "metadata": {}, + "source": [ + "# `equilibrium.py`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/examples.ipynb b/docs/dev_guide/notebooks/examples.ipynb new file mode 100644 index 0000000000..80c3ba9dcf --- /dev/null +++ b/docs/dev_guide/notebooks/examples.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4f884b0c-bb29-43ee-9e84-1421a34a4f5b", + "metadata": {}, + "source": [ + "# `examples`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/geometry.ipynb b/docs/dev_guide/notebooks/geometry.ipynb new file mode 100644 index 0000000000..10c645541f --- /dev/null +++ b/docs/dev_guide/notebooks/geometry.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7c68566a-292c-446e-ad12-5a609a92f7f1", + "metadata": {}, + "source": [ + "# `geometry`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/dev_guide/grid.ipynb b/docs/dev_guide/notebooks/grid.ipynb similarity index 99% rename from docs/notebooks/dev_guide/grid.ipynb rename to docs/dev_guide/notebooks/grid.ipynb index 91b231f636..61691cbe61 100644 --- a/docs/notebooks/dev_guide/grid.ipynb +++ b/docs/dev_guide/notebooks/grid.ipynb @@ -20,7 +20,7 @@ "id": "3c115859-2c06-47b2-9163-ce1b6d912662", "metadata": {}, "source": [ - "## The grid has 2 jobs.\n", + "The grid has 2 jobs.\n", "\n", "- Node placement\n", "- Node weighting\n", @@ -140,7 +140,7 @@ "\n", "plot_grid(lg)\n", "plot_grid(qg)\n", - "plot_grid(cg)" + "plot_grid(cg);" ] }, { @@ -1489,7 +1489,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "desc-env", "language": "python", "name": "python3" }, @@ -1503,7 +1503,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.4" + "version": "3.12.0" }, "toc-autonumbering": true, "toc-showcode": true, diff --git a/docs/dev_guide/notebooks/interpolate.ipynb b/docs/dev_guide/notebooks/interpolate.ipynb new file mode 100644 index 0000000000..47d9877de5 --- /dev/null +++ b/docs/dev_guide/notebooks/interpolate.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "504fd612-4dbc-4706-aed7-9f3d7cf50449", + "metadata": {}, + "source": [ + "# `interpolate.py`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/io.ipynb b/docs/dev_guide/notebooks/io.ipynb new file mode 100644 index 0000000000..537d99427e --- /dev/null +++ b/docs/dev_guide/notebooks/io.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c972890f-ee7e-4232-b598-cc1ac65cae26", + "metadata": {}, + "source": [ + "# `io`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/magnetic_fields.ipynb b/docs/dev_guide/notebooks/magnetic_fields.ipynb new file mode 100644 index 0000000000..54fb24fc18 --- /dev/null +++ b/docs/dev_guide/notebooks/magnetic_fields.ipynb @@ -0,0 +1,49 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "aaa2cb32-b4d9-43da-91e2-5b1f8f2a26e3", + "metadata": { + "tags": [] + }, + "source": [ + "# `magnetic_fields.py`" + ] + }, + { + "cell_type": "markdown", + "id": "a2155d59-942c-4263-8295-9a851b4143fc", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76ea7fa2-e5ce-42aa-a866-139bd2050064", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/objectives.ipynb b/docs/dev_guide/notebooks/objectives.ipynb new file mode 100644 index 0000000000..a2059b1a6e --- /dev/null +++ b/docs/dev_guide/notebooks/objectives.ipynb @@ -0,0 +1,145 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b69ed88a-4ee1-438b-b992-4f57e22613c3", + "metadata": { + "tags": [], + "toc-hr-collapsed": true + }, + "source": [ + "# `objectives`" + ] + }, + { + "cell_type": "markdown", + "id": "495554b5-2655-47c8-8e25-a758273b9df9", + "metadata": {}, + "source": [ + "talk about obj vs constraints\n", + "- [ ] why do we need to re-make the objectives when we change eq resolutoin\n", + " - I think this is because the objectives get built for that eq (built = ?) , so remaking them means we get rid of the built status?\n", + "- is the size of the `x_reduced` equal to number of parameters? YES IT IS And is this equal to one of the sides of the linear constraint matrix `A`? it is in `factorize_linear_constraints` which is in `objectives.utils`, from `project` function" + ] + }, + { + "cell_type": "markdown", + "id": "16789fcf-99a9-47ba-8480-e628f40a74e9", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83fc3e85-3985-4cc5-86b4-cc47fd76edf6", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "263199ee-3a30-4390-8487-9083853393ba", + "metadata": {}, + "source": [ + "## `linear_objectives.py`" + ] + }, + { + "cell_type": "markdown", + "id": "25f3e1cc-c637-47d3-b24e-44002984608a", + "metadata": {}, + "source": [ + "- when specifying interior surface as the fixboundary constraint, the self A becomes zernike_radial instead of 1?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2b30d7d-fd05-407c-90e3-2e4fe29ab18b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "a136e3b4-2237-4525-8a72-4dd9eba471ab", + "metadata": {}, + "source": [ + "## `objectives/utils.py`" + ] + }, + { + "cell_type": "markdown", + "id": "34d9f66e-48bb-4b9d-b962-b88c919e28ea", + "metadata": {}, + "source": [ + "#### `factorize_linear_constraints`" + ] + }, + { + "cell_type": "markdown", + "id": "36225c78-8ae2-4576-ae06-1f81382476b6", + "metadata": {}, + "source": [ + " - define problem in standard optimization setup\n", + " - match which DESC variables are equal to the standard ones\n", + " - write Ax=b\n", + " - this thing basically finds the nullspace( A )=: Z\n", + " - Z then can be multiplied with (x - x_particular) to obtain a vector which is guaranteed to be in the nullspace of the constraints, bc it is made up of a linear combinations of vectors which lie inside of the nullspace (does this make sense?)..." + ] + }, + { + "cell_type": "markdown", + "id": "cd873863-075f-480f-9c60-56aa5f0c1f38", + "metadata": {}, + "source": [ + "DESC approaches the ideal MHD fixed-boundary equilibrium problem $\\mathbf{F}=0$ [as an optimization problem](https://desc-docs.readthedocs.io/en/latest/theory_general.html):\n", + "\n", + "$$\\begin{align}\n", + "\\min_{\\mathbf{x}\\in \\mathcal{R}^n} \\mathbf{f}(\\mathbf{x})&\\\\\n", + "\\text{subject to the linear constraints}~~ \\mathbf{A}\\mathbf{x}=\\mathbf{b}&\n", + "\\end{align}$$\n", + "\n", + "where the objective to be minimized is the MHD force balance error $\\mathbf{F}=\\mathbf{J}\\times \\mathbf{B} - \\nabla p$, which is minimized by evaluating the two components of $\\mathbf{F}$ on a collocation grid (resulting in a vector of residuals $\\mathbf{f} = [f_{\\rho},f_{\\beta}]$ of length `2*num_nodes` since each of $f_{\\rho},f_{\\beta}$ are evaluated at the collocation nodes) and then minimizing those residuals $\\mathbf{f}(\\mathbf{x})$. \n", + "The state variable being minimized over $\\mathbf{x} = [R_{lmn}, Z_{lmn}, \\lambda_{lmn}]$ is the vector of the Fourier-Zernike spectral coefficients used to describe the mapping between the toroidal $(R,\\phi,Z)$ coordinates and the computational flux coordinates $(\\rho,\\theta,\\zeta)$.\n", + "The state is of length `3*eq.R_basis.num_modes` (if a non-stellarator symmetric equilibrium, where the number of basis modes for R and Z are the same), or length `eq.R_basis.num_modes + 2 * eq.Z_basis.num_modes` (if a stellarator-symmetric equilibrium, where $R$ has $cos(m\\theta-n\\zeta)$ symmetry and $Z$ and $\\lambda$ have $sin(m\\theta-n\\zeta)$).\n", + "\n", + "\n", + "A fixed-boundary equilbrium problem requires the fixed-boundary $R_b(\\theta,\\zeta),Z_b{\\theta,\\zeta}$ to be given as the input, and this appears as a linear constraint on $\\mathbf{x}$ during the optimization. \n", + "In DESC, additionally a [gauge constraint](https://desc-docs.readthedocs.io/en/latest/_api/objectives/desc.objectives.FixLambdaGauge.html) on $\\lambda$ is applied, since $\\lambda$ is only defined up to an additive multiple of $2\\pi$, which constitutes another linear constraint to the problem. \n", + "The linear constraints are then written in the form $\\mathbf{A}\\mathbf{x}=\\mathbf{b}$. " + ] + }, + { + "cell_type": "markdown", + "id": "3a9858ff-3cae-4158-91ce-27f19e6199b6", + "metadata": {}, + "source": [ + "In solving for the equilibrium DESC deals with these linear constraints by using the feasible direction formulation (see for example [page 3 of this reference](https://www.cs.umd.edu/users/oleary/a607/607constr1hand.pdf)).\n", + "\n", + "The state variable $\\mathbf{x}$ is written as $\\mathbf{x} = \\mathbf{x}_p + \\mathbf{Z}\\mathbf{y}$" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/optimization_objectives_constraints.ipynb b/docs/dev_guide/notebooks/optimization_objectives_constraints.ipynb new file mode 100644 index 0000000000..2f6a5ad000 --- /dev/null +++ b/docs/dev_guide/notebooks/optimization_objectives_constraints.ipynb @@ -0,0 +1,294 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b69ed88a-4ee1-438b-b992-4f57e22613c3", + "metadata": { + "tags": [], + "toc-hr-collapsed": true + }, + "source": [ + "# Optimization, objectives, and constraints" + ] + }, + { + "cell_type": "markdown", + "id": "8aa50bd6-6c18-41ff-a891-92785359fb97", + "metadata": {}, + "source": [ + "The goal of any unconstrained optimization problem is to find the \"best\" solution or most desirable input values for a given objective function.\n", + "In a constrained optimization problem, there is an additional set of constraints that must be satified for the solution to be of interest.\n", + "\n", + "DESC approaches the ideal MHD fixed-boundary equilibrium problem $\\mathbf{F}=0$ as an optimization problem (see Theory docs).\n", + "That is $\\min_{\\mathbf{x} \\in \\mathcal{R}^n} \\mathbf{f}(\\mathbf{x})$ subject to a system of linear constraints $\\mathbf{A}\\mathbf{x}=\\mathbf{b}$ where the objective to be minimized is the MHD force balance error $\\mathbf{F}=\\mathbf{J}\\times \\mathbf{B} - \\nabla p$.\n", + "\n", + "This objective is minimized by evaluating the two components of $\\mathbf{F}$, given by $f_{\\rho}$ and $f_{\\beta}$, on a collocation grid.\n", + "The resulting vector of residuals $\\mathbf{f} = [f_{\\rho},f_{\\beta}]$ has length equal to twice the number of grid points (colloaction nodes) since each of $f_{\\rho}, f_{\\beta}$ are evaluated at every collocation node.\n", + "\n", + "The two components of the force balance residuals map the state vector $\\mathbf{x}$ to the values of the residuals at the points given by the state vector: $f \\colon \\mathbf{x} ↦ f(\\mathbf{x})$. \n", + "The state vector being minimized over $\\mathbf{x} = [R_{lmn}, Z_{lmn}, \\lambda_{lmn}]$ is the vector of the Fourier-Zernike spectral coefficients used to describe the mapping between the toroidal $(R,\\phi,Z)$ coordinates and the computational flux coordinates $(\\rho,\\theta,\\zeta)$.\n", + "The state vector has one of the following lengths\n", + "- `3*eq.R_basis.num_modes` if a non-stellarator symmetric equilibrium, where the number of basis modes for R and Z are the same\n", + "- `eq.R_basis.num_modes + 2 * eq.Z_basis.num_modes` if a stellarator-symmetric equilibrium, where $R$ has $cos(m\\theta-n\\zeta)$ symmetry, and $Z$ and $\\lambda$ have $sin(m\\theta-n\\zeta)$" + ] + }, + { + "cell_type": "markdown", + "id": "5f36f11d-ebd3-4a35-90fb-d1b398704467", + "metadata": { + "tags": [] + }, + "source": [ + "## Objectives vs. constraints\n", + "\n", + "A typical task for DESC may involve\n", + "- solving for a good equilibrium (minimize force balance errors) given constraints like profiles and boundaries\n", + "- optimizing for some criteria on the solved equilibrium\n", + "\n", + "The first task would include `ForceBalance()` as the objective function and constriants which fix profiles and boundaries.\n", + "A fixed-boundary equilbrium problem requires the fixed-boundary $R_b(\\theta,\\zeta),Z_b({\\theta,\\zeta})$ to be given as a linear constraint during the optimization.\n", + "In DESC, additionally a `gauge constraint` on $\\lambda$ is applied (to make it periodic), since $\\lambda$ is only defined up to an additive multiple of $2\\pi$, which constitutes another linear constraint to the problem.\n", + "\n", + "The second task may consider `ForceBalance()` as a constraint, so as to not throw away the work done to find a good equilibrium, and some criteria for better quasisymmetry as an objective.\n", + "This allows for searching the configuration space (combinations of parameters that define the state of plasma) for configurations with better quasisymmetry while only considering those that are still good equilibriums.\n", + "\n", + "As demonstrated above, the python object `ForceBalance()` of type `Objective` was used as an objective in the optimization sense in the first task and a constraint in the second task.\n", + "There is no seperate `Constraint` type class.\n", + "An `Objective` type object is an optimization objective if it is supplied for the `objective` argument to the `optimizer` object.\n", + "An `Objective` type object is an optimization constraint if it is supplied for the `constraints` argument to the `optimizer` object." + ] + }, + { + "cell_type": "markdown", + "id": "426436c8-f966-4a68-8480-7f6d5563a690", + "metadata": {}, + "source": [ + "## Feasible direction formulation\n", + "\n", + "In any case, the task given to DESC is to solve a constrained optimization problem.\n", + "DESC deals with constrained optimization problem by using the feasible direction formulation.\n", + "See for example [page 3 of this reference](https://www.cs.umd.edu/users/oleary/a607/607constr1hand.pdf).\n", + "\n", + "The geometry of this approach is as follows.\n", + "Suppose the objective is to minimize a function $f \\colon \\mathbb{R}^n \\to \\mathbb{R}$, subject to a linear system of equations that define the constraints given by $A \\mathbf{x} = \\mathbf{b}$.\n", + "These equations sketch a surface: $\\text{image}(A) = S \\subset \\mathbb{R}^n$ that define the set of feasible points.\n", + "That is, any point on this surface satisfies $A \\mathbf{x} = \\mathbf{b}$.\n", + "It is more practical to search for minima to $f$ on this surface than blindy through $\\mathbb{R}^n$.\n", + "\n", + "With this approach, the iteration defined by the optimization will only consider vectors that are valid candidates (they satisfy the constraints).\n", + "Moreover, by only searching for solutions on this surface, we can reduce the constrained optimization problem\n", + "$$\\min_{\\mathbf{x} ∈ \\mathbb{R}^n} f(\\mathbf{x}) \\; \\text{such that} \\; A \\mathbf{x} = \\mathbf{b}$$\n", + "to an unconstrained one which can be solved with techniques like Newton iteration and least-squares.\n", + "Moreover, each step of the iteration may be a potential solution.\n", + "$$\\min_{\\mathbf{x} ∈ S \\subset \\mathbb{R}^n} f(\\mathbf{x})$$" + ] + }, + { + "cell_type": "markdown", + "id": "55080848-eed0-4e6f-b993-d6e148db919a", + "metadata": { + "tags": [] + }, + "source": [ + "## Removing linear constraints by factoring\n", + "We can limit the search space to the relavant surface by factoring the state vector into a particular component and a homogenous component: $\\mathbf{x} = \\mathbf{x}_{\\text{p}} + \\mathbf{x}_{\\text{h}}$.\n", + "The particular component, $\\mathbf{x}_{\\text{p}}$, satisfies the constraints $A \\mathbf{x}_{\\text{p}} = \\mathbf{b}$.\n", + "Meaning $\\mathbf{x}_{\\text{p}}$ is a vector that points from the origin to a point on the surface $S = \\text{image}(A)$.\n", + "The homogenous component, $\\mathbf{x}_{\\text{h}}$, satisfies $A \\mathbf{x}_{\\text{h}} = \\mathbf{0}$.\n", + "Meaning $\\mathbf{x}_{\\text{h}}$ is a vector that points from some point on $S$ to another point on $S$.\n", + "Hence, $\\mathbf{x}_p + \\mathbf{x}_h$ lies on the surface $S$, or in the image of $A$.\n", + "$$A \\mathbf{x} = A (\\mathbf{x}_{\\text{p}} + \\mathbf{x}_{\\text{h}}) = \\mathbf{b}$$\n", + "\n", + "Any $\\mathbf{x}_{\\text{h}}$ can be written as a linear combination of a nullspace basis of $A$: $\\; \\mathbf{x}_{\\text{h}} = Z \\mathbf{y}$.\n", + "These are the vectors which parameterize the surface $S$.\n", + "With this convention, the state variable is\n", + "$$\\mathbf{x} = \\mathbf{x}_p + \\mathbf{Z}\\mathbf{y}$$\n", + "and the optimization problem becomes an unconstrained search for $\\mathbf{y}$ that yields\n", + "$$\\min_{\\mathbf{y} ∈ \\mathbb{R}^{n - m} \\subset \\mathbb{R}^n} f(\\mathbf{x}_{\\text{p}} + Z \\mathbf{y})$$\n", + "\n", + "The length of the vector $\\mathbf{y}$ corresponds to the number of linearly independent vectors in the nullspace of $A$, or the number of free (unfixed) parameters.\n", + "If $A \\in \\mathbb{R}^{m \\times n}$ with rank $m$, then the length of $\\mathbf{y}$ will be $n-m$.\n", + "Each component of $\\mathbf{y}$ corresponds to some unfixed parameter.\n", + "As the optimizer iterates through some trajectory by changing these parameters, the optimizer searches over the surface of feasible solutions.\n", + "\n", + "This method is sometimes summarized as projecting away the constraints because the orthogonal projection of $\\mathbf{x} - \\mathbf{x}_p$ onto the nullspace of $A$ is $\\mathbf{x}_h$.\n", + "If $Z$ is additionally constructed to be orthonormal, then it is easy to compute $\\mathbf{y}$ from $\\mathbf{x}$ and vice versa, a desireable quality to recover the solution to the original optimization problem from the simpler one it was reduced to.\n", + "Recall that $Z Z^T$ is the orthogonal projection onto the nullspace of $A$.\n", + "We have\n", + "$$ \\begin{align*}\n", + " \\mathbf{x} - \\mathbf{x}_p & = \\mathbf{x}_h \\\\\n", + " & = Z \\mathbf{y} \\\\\n", + " Z Z^T (\\mathbf{x} - \\mathbf{x}_p) & = Z (Z^T Z) \\mathbf{y} \\\\\n", + " & = Z \\mathbf{y} \\\\\n", + " & = \\mathbf{x}_h\n", + "\\end{align*} $$\n", + "The easy way to compute $\\mathbf{y}$, or to \"project\" the full state vector $\\mathbf{x}$ into the reduced optimization vector $\\mathbf{y}$, is:\n", + "$$Z^T (\\mathbf{x} - \\mathbf{x}_p) = \\mathbf{y}$$" + ] + }, + { + "cell_type": "markdown", + "id": "3193e76f-4662-4610-98df-e310d89fd23a", + "metadata": {}, + "source": [ + "### `factorize_linear_constraints`\n", + "\n", + "In DESC, the process discussed above is done in the `factorize_linear_constraints` function of `desc.objectives.utils`.\n", + "This next few paragraphs will walk through the important parts of that code.\n", + "\n", + "A given optimization may have multiple constraints.\n", + "The \"parallel-arrays\" convention is used to group them.\n", + "There are dictionaries denoted $A$, $\\mathbf{x}$, and $\\mathbf{b}$ where the keys are the names of the constraints (`obj.args[0]` is the class name of the objective), and the values are the matrices associated with that constraint.\n", + "So the constraint equations associated with a magnetic well constraint could be queried with:\n", + "```python\n", + "key = \"Magnetic Well\"\n", + "A_key = A[key]\n", + "xp_key = xp[key]\n", + "b_key = b[key]\n", + "```\n", + "\n", + "First, we instantiate the dictionaries and create the vectors for each $\\mathbf{x}$ of every constraint.\n", + "The `dim_x` attribute of an `objective` is the length of the state vector $\\mathbf{x}$.\n", + "The `dim_f` attribute is the number of objective equations or the rank of $A$ in $A \\mathbf{x} = \\mathbf{b}$ when the `objective` object is considered a constraint.\n", + "```python\n", + "# set state vector\n", + "args = np.concatenate([obj.args for obj in constraints])\n", + "args = np.concatenate((args, objective_args))\n", + "# this is all args used by both constraints and objective\n", + "args = [arg for arg in arg_order if arg in args]\n", + "dimensions = constraints[0].dimensions\n", + "dim_x = 0\n", + "x_idx = {}\n", + "for arg in objective_args:\n", + " x_idx[arg] = np.arange(dim_x, dim_x + dimensions[arg])\n", + " dim_x += dimensions[arg]\n", + "\n", + "A = {}\n", + "b = {}\n", + "Ainv = {}\n", + "xp = jnp.zeros(dim_x) # particular solution to Ax=b\n", + "constraint_args = [] # all args used in constraints\n", + "unfixed_args = [] # subset of constraint args for unfixed objectives\n", + "```\n", + "\n", + "Then we loop through each constraint and create the matrices as discussed above.\n", + "Note that if the target vector is fully specified, that is the length given by `dimensions[obj.target.arg]` equals the total number of available equations, then we know the target vector should be a solution to the contraints $A \\mathbf{x} = \\mathbf{b}$ and can set $\\mathbf{x}_p$ to match the target vector.\n", + "\n", + "Otherwise, the `else` loop is entered, and we need to actually solve for a solution to the system.\n", + "Recall, the compute functions of `Objective` objects first compute some quantity (like $A \\mathbf{x}$), then they call the method `self._shift_scale` to subtract out the target quantity.\n", + "In other words, the function call `obj.compute_scaled(x)` computes the value of a function of $\\mathbf{x}$ defined as $A \\mathbf{x} - \\mathbf{b}$, which we would prefer to be close to zero.\n", + "To compute $\\mathbf{b}$, we may store the returned value of the function call: `-1 * obj.compute_scaled(x=0)`.\n", + "\n", + "```python\n", + "# linear constraint matrices for each objective\n", + "for obj in constraints:\n", + " if obj.fixed and obj.dim_f == dimensions[obj.target_arg]:\n", + " # obj.fixed is always true if the objective is linear\n", + " # if all coefficients are fixed the constraint matrices are not needed\n", + " xp = put(xp, x_idx[obj.target_arg], obj.target)\n", + " else:\n", + " unfixed_args.append(arg)\n", + " A_ = obj.derivatives[\"jac\"][arg](jnp.zeros(dimensions[arg]))\n", + " # using obj.compute instead of obj.target to allow for correct scale/weight\n", + " b_ = -obj.compute_scaled(jnp.zeros(obj.dimensions[arg]))\n", + " Ainv_, Z_ = svd_inv_null(A_)\n", + " A[arg] = A_\n", + " b[arg] = b_\n", + " # need to undo scaling here to work with perturbations\n", + " Ainv[arg] = Ainv_ * obj.weight / obj.normalization\n", + "```\n", + "\n", + "Then we merge each individual constraint matrix into one block diagonal system.\n", + "That way we can return a single optimization problem back to optimizer.\n", + "```python\n", + "# full A matrix for all unfixed constraints\n", + "if len(A):\n", + " unfixed_idx = jnp.concatenate(\n", + " [x_idx[arg] for arg in arg_order if arg in A.keys()]\n", + " )\n", + " A_full = block_diag(*[A[arg] for arg in arg_order if arg in A.keys()])\n", + " b_full = jnp.concatenate([b[arg] for arg in arg_order if arg in b.keys()])\n", + " Ainv_full, Z = svd_inv_null(A_full)\n", + " xp = put(xp, unfixed_idx, Ainv_full @ b_full)\n", + "```\n", + "\n", + "The helper function used above, `desc.utils.svd_inv_null(A)`, returns the pseudoinverse, $A^{\\dagger}$, and an orthonormal matrix $Z$ with columns that span the nullspace of A.\n", + "We then return functions to the optimizer that compute the reduced optimization vector `x_reduced` (labeled $\\mathbf{y}$ in the math discussed above) and recover the full state vector.\n", + "\n", + "```python\n", + "def project(x):\n", + " \"\"\"Project a full state vector into the reduced optimization vector.\"\"\"\n", + " x_reduced = Z.T @ ((x - xp)[unfixed_idx])\n", + " return jnp.atleast_1d(jnp.squeeze(x_reduced))\n", + "\n", + "def recover(x_reduced):\n", + " \"\"\"Recover the full state vector from the reduced optimization vector.\"\"\"\n", + " dx = put(jnp.zeros(dim_x), unfixed_idx, Z @ x_reduced)\n", + " return jnp.atleast_1d(jnp.squeeze(xp + dx))\n", + "```\n", + "\n", + "It should be clear that the length of `x_reduced` is equal to the number of free (unfixed) parameters." + ] + }, + { + "cell_type": "markdown", + "id": "16789fcf-99a9-47ba-8480-e628f40a74e9", + "metadata": {}, + "source": [ + "## Rebuilding objectives\n", + "DESC uses a iterative method to solve and optimizize equilibrium.\n", + "Sometimes this invovles changing the resolution of an equilibrium via changing the resolution of the `grid` object belonging to that equilibrium.\n", + "(Typlically we start from a low resolution equilibrium, and increase later).\n", + "This means many quantities need to be recomputed on the newer grid.\n", + "In particular the `Objective` objects that include optimization objectives and constraints need to be rebuilt, so that the values they store are computed on the newer grid.\n", + "(See the grid tutorial if you are not familiar with the structure in which computed quantities are stored)." + ] + }, + { + "cell_type": "markdown", + "id": "eee1e448-a8a5-4a4f-9f44-d7c3f8a24f7c", + "metadata": {}, + "source": [ + "# todo below\n", + "https://web.stanford.edu/~boyd/papers/pdf/prox_algs.pdf" + ] + }, + { + "cell_type": "markdown", + "id": "263199ee-3a30-4390-8487-9083853393ba", + "metadata": {}, + "source": [ + "## `linear_objectives.py`" + ] + }, + { + "cell_type": "markdown", + "id": "25f3e1cc-c637-47d3-b24e-44002984608a", + "metadata": {}, + "source": [ + "- when specifying interior surface as the fixboundary constraint, the self A becomes zernike_radial instead of 1?" + ] + } + ], + "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.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/optimize.ipynb b/docs/dev_guide/notebooks/optimize.ipynb new file mode 100644 index 0000000000..969595c48a --- /dev/null +++ b/docs/dev_guide/notebooks/optimize.ipynb @@ -0,0 +1,35 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f5f34527-1cb1-4c99-9035-d19323c9a239", + "metadata": { + "tags": [] + }, + "source": [ + "# `optimize`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/perturbations.ipynb b/docs/dev_guide/notebooks/perturbations.ipynb new file mode 100644 index 0000000000..c9dc5254af --- /dev/null +++ b/docs/dev_guide/notebooks/perturbations.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e8aa087f-8654-412f-8b08-4469dce1f2c6", + "metadata": {}, + "source": [ + "# `perturbations.py`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/plotting.ipynb b/docs/dev_guide/notebooks/plotting.ipynb new file mode 100644 index 0000000000..96ff30b1b7 --- /dev/null +++ b/docs/dev_guide/notebooks/plotting.ipynb @@ -0,0 +1,37 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c36e49d4-2df6-4890-b95b-23d7a72d5095", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "# `plotting.py`\n", + "maybe pretty self-explanatory" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/transform.ipynb b/docs/dev_guide/notebooks/transform.ipynb new file mode 100644 index 0000000000..6a6595aa7f --- /dev/null +++ b/docs/dev_guide/notebooks/transform.ipynb @@ -0,0 +1,136 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fbb1cce6-2f0a-49c7-a80a-212408ee20b0", + "metadata": { + "tags": [], + "toc-hr-collapsed": true + }, + "source": [ + "# `transform.py`" + ] + }, + { + "cell_type": "markdown", + "id": "ea2f4743-77f8-4e3f-b80c-1c28c1489189", + "metadata": {}, + "source": [ + " - what the transform is\n", + " - has a basis and a grid, and evaluates that basis on its grid right? so it contains the transform matrices (call them transform matrices)? how are these called? but these are the matrices which, when multiplied against a vector of the coefficients of the spectral series, yields the series evaluated at the grid points" + ] + }, + { + "cell_type": "markdown", + "id": "e2d02571-b24d-485c-90de-495fe4b9a302", + "metadata": {}, + "source": [ + "this file contains the `Transform` class. " + ] + }, + { + "cell_type": "markdown", + "id": "6a92e963-a6da-4268-9a53-b277f9a80691", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f888cc17-ba5b-414e-b83e-772eaf510ca6", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "00496ec1-77f7-4266-aaf6-c037e14a223d", + "metadata": {}, + "source": [ + "## `build()`" + ] + }, + { + "cell_type": "markdown", + "id": "6dbcf2ef-2649-40df-9f44-c2df820ec260", + "metadata": {}, + "source": [ + "this method builds the transform matrices for each derivative order of the basis the transform requires (which is specified when the `Transform` object is initialized with the `derivs` argument).\n", + "Defining the transform matrix as $A_{(d\\rho,d\\theta,d\\zeta)}$ for given derivatives of the basis ${(d\\rho,d\\theta,d\\zeta)}$ (where each are integers), the matrix is used to transform a spectral basis with a given set of coefficients $\\mathbf{c}$ into its values on the grid (given by `Transform.grid`) by \n", + "\n", + "$$ A\\mathbf{c} = \\mathbf{x}$$\n", + "\n", + "where $\\mathbf{x}$ is the values of the spectral basis evaluated at the grid points . \n", + "$\\mathbf{c}$ is a vector of length `Transform.basis.num_modes` (the number of modes in the basis), $\\mathbf{x}$ is a vector of length `Transform.grid.num_nodes` (the number of nodes in the grid), and $A$ is a matrix of shape `(num_nodes,num_modes)`.\n", + "\n", + "\n", + "i.e. if a Fourier Series $f(\\zeta) = 2 + 4*cos(\\zeta)$, and the grid is $\\zeta = (0,\\pi)$, then $\\mathbf{x} =\\begin{bmatrix}0\\\\ \\pi\\end{bmatrix}$, $\\mathbf{c}=\\begin{bmatrix} 2\\\\ 4 \\end{bmatrix}$, and $A = \\begin{bmatrix} 1 & cos(0)\\\\ 1& cos(\\pi) \\end{bmatrix} = \\begin{bmatrix} 1& 1\\\\ 1& -1 \\end{bmatrix} $ s.t. \n", + "$$ A\\mathbf{c} = \\begin{bmatrix} 1& 1\\\\ 1& -1 \\end{bmatrix} \\begin{bmatrix} 2\\\\ 4 \\end{bmatrix} = \\begin{bmatrix} 6 \\\\ -2 \\end{bmatrix} $$\n" + ] + }, + { + "cell_type": "markdown", + "id": "6a8f43e3-9ee8-449b-b6ea-4672a966d914", + "metadata": { + "tags": [] + }, + "source": [ + "## `build_pinv()`" + ] + }, + { + "cell_type": "markdown", + "id": "261815bc-a394-4c11-a5c0-9dc127b37b25", + "metadata": {}, + "source": [ + "This function builds the pseudoinverse of the transform, which can then be used to take values of a given function that are given on `Transform.grid` and fit a spectral basis to them.\n", + "A couple different methods are available:" + ] + }, + { + "cell_type": "markdown", + "id": "a05153d1-b6d6-413e-aea9-f08d6cd1a627", + "metadata": {}, + "source": [ + "### `direct1`" + ] + }, + { + "cell_type": "markdown", + "id": "019404de-7732-4c92-be02-b29c66310225", + "metadata": {}, + "source": [ + "With this method, " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c774a214-f846-4f26-a851-86013eb702fa", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/utils.ipynb b/docs/dev_guide/notebooks/utils.ipynb new file mode 100644 index 0000000000..c6c7c2bf07 --- /dev/null +++ b/docs/dev_guide/notebooks/utils.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c07f1b20-0d8a-49e8-9f18-644843bfe344", + "metadata": {}, + "source": [ + "# `utils.py`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/vmec.ipynb b/docs/dev_guide/notebooks/vmec.ipynb new file mode 100644 index 0000000000..963e21efcf --- /dev/null +++ b/docs/dev_guide/notebooks/vmec.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bcc198fa-cc67-45ba-bc6e-13d88d6c4caa", + "metadata": {}, + "source": [ + "# `vmec.py`" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/notebooks/vmec_utils.ipynb b/docs/dev_guide/notebooks/vmec_utils.ipynb new file mode 100644 index 0000000000..c914fda2ec --- /dev/null +++ b/docs/dev_guide/notebooks/vmec_utils.ipynb @@ -0,0 +1,41 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c64652e1-9cca-44b1-bbda-b9133754ae9b", + "metadata": {}, + "source": [ + "# `vmec_utils.py`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c774a214-f846-4f26-a851-86013eb702fa", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/dev_guide/objectives.rst b/docs/dev_guide/objectives.rst new file mode 100644 index 0000000000..fb25d363ee --- /dev/null +++ b/docs/dev_guide/objectives.rst @@ -0,0 +1,202 @@ +============================== +Adding new objective functions +============================== + +This guide walks through creating a new objective to optimize using Quasi-symmetry as +an example. The primary methods needed for a new objective are ``__init__``, ``build``, +and ``compute``. The base class ``_Objective`` provides a number of other methods that +generally do not need to be re-implemented for subclasses. + +``__init__`` should generally just assign attributes and store inputs. It should not do +any expensive calculations, these should be in ``build`` or ``compute``. The main arguments +are summarized in the example below. + +``build`` is called before optimization with the ``Equilibrium`` to be optimized. +It is used to precompute things like transform matrices that convert between spectral +coefficients and real space values. +In the build method, we first ensure that a ``Grid`` is assigned, using default values +from the equilibrium if necessary. The grid defines the points in flux coordinates where +we evaluate the residuals. +Next, we define the physics quantities we need to evaluate the objective (``_data_keys``), +and the number of residuals that will be returned by ``compute`` (``_dim_f``). +Next, we use some helper functions to build the required ``Tranform`` and ``Profile`` +objects needed to compute the desired physics quantities. +Finally, we call the base class ``build`` method to do some checking of array sizes and +other misc. stuff. + +``compute`` is where the actual calculation of the residual takes place. Objectives +generally return a vector of residuals that are minimized in a least squares sense, though +the exact method will depend on the optimization algorithm. The main thing here is +calling ``compute_fun`` to get physics quantities, and then performing any post-processing +we want such as averaging, combining, etc. The final step is to call ``self._shift_scale`` +which subtracts out the target and applies weighting and normalizations. + +A full example objective with comments describing key points is given below: +:: + + from desc.objectives.objective_funs import _Objective + from desc.objectives.normalization import compute_scaling_factors + from desc.compute.utils import get_params, get_profiles, get_transforms + from desc.compute import compute as compute_fun + + + class QuasisymmetryTripleProduct(_Objective): # need to subclass from ``desc.objectives._Objective`` + """Give a description of what it is and what it's useful for. + + Parameters + ---------- + eq : Equilibrium, optional + Equilibrium that will be optimized to satisfy the Objective. + target : float, ndarray, optional + Target value(s) of the objective. + len(target) must be equal to Objective.dim_f + weight : float, ndarray, optional + Weighting to apply to the Objective, relative to other Objectives. + len(weight) must be equal to Objective.dim_f + normalize : bool + Whether to compute the error in physical units or non-dimensionalize. + normalize_target : bool + Whether target should be normalized before comparing to computed values. + if `normalize` is `True` and the target is in physical units, this should also + be set to True. + grid : Grid, ndarray, optional + Collocation grid containing the nodes to evaluate at. + name : str + Name of the objective function. + + """ + + _scalar = False # does self.compute return a scalar or vector? + _linear = False # is self.compute a linear function of its parameters? + _units = "(T^4/m^2)" # units of the output + _print_value_fmt = "Quasi-symmetry error: {:10.3e} " # string with python string formatting for printing the value + + def __init__( + self, + eq=None, + target=0, + weight=1, + normalize=True, + normalize_target=True, + grid=None, + name="QS triple product", + ): + + # we don't have to do much here, mostly just call ``super().__init__()`` + # to inherit common initialization logic from ``desc.objectives._Objective`` + self.grid = grid + super().__init__( + eq=eq, + target=target, + weight=weight, + normalize=normalize, + normalize_target=normalize_target, + name=name, + ) + + def build(self, eq, use_jit=True, verbose=1): + """Build constant arrays. + + Parameters + ---------- + eq : Equilibrium, optional + Equilibrium that will be optimized to satisfy the Objective. + use_jit : bool, optional + Whether to just-in-time compile the objective and derivatives. + verbose : int, optional + Level of output. + + """ + # need some sensible default grid + if self.grid is None: + self.grid = LinearGrid(M=eq.M_grid, N=eq.N_grid, NFP=eq.NFP, sym=eq.sym) + + # dim_f = size of the output vector returned by self.compute + # self.compute refers to the objective's own compute method + # Typically an objective returns the output of a quantity computed in + # ``desc.compute``, with some additional scale factor. + # In these cases dim_f should match the size of the quantity calculated in + # ``desc.compute`` (for example self.grid.num_nodes). + # If the objective does post-processing on the quantity, like downsampling or + # averaging, then dim_f should be changed accordingly. + # What data from desc.compute is needed? Here we want the QS triple product. + self._data_keys = ["f_T"] + # what arguments should be passed to self.compute + self._args = get_params(self._data_keys) + + # some helper code for profiling and logging + timer = Timer() + if verbose > 0: + print("Precomputing transforms") + timer.start("Precomputing transforms") + + # helper functions for building transforms etc to compute given + # quantities. Alternatively, these can be created manually based on the + # equilibrium, though in most cases that isn't necessary. + self._profiles = get_profiles(self._data_keys, eq=eq, grid=self.grid) + self._transforms = get_transforms(self._data_keys, eq=eq, grid=self.grid) + + timer.stop("Precomputing transforms") + if verbose > 1: + timer.disp("Precomputing transforms") + + + # We try to normalize things to order(1) by dividing things by some + # characteristic scale for a given quantity. + # See ``desc.objectives.compute_scaling_factors`` for examples. + if self._normalize: + scales = compute_scaling_factors(eq) + # since the objective has units of T^4/m^2, the normalization here is + # based on a characteristic field strength and minor radius. + # we also divide by the square root of number of residuals to keep + # things roughly independent of the grid resolution. + self._normalization = ( + scales["B"] ** 4 / scales["a"] ** 2 / jnp.sqrt(self._dim_f) + ) + + # finally, call ``super.build()`` + super().build(eq=eq, use_jit=use_jit, verbose=verbose) + + def compute(self, *args, **kwargs): + """Signature should only take args and kwargs, but you can use the Parameters + block below to specify what these should be. + + Parameters + ---------- + R_lmn : ndarray + Spectral coefficients of R(rho,theta,zeta) -- flux surface R coordinate (m). + Z_lmn : ndarray + Spectral coefficients of Z(rho,theta,zeta) -- flux surface Z coordinate (m). + L_lmn : ndarray + Spectral coefficients of lambda(rho,theta,zeta) -- poloidal stream function. + i_l : ndarray + Spectral coefficients of iota(rho) -- rotational transform profile. + c_l : ndarray + Spectral coefficients of I(rho) -- toroidal current profile. + Psi : float + Total toroidal magnetic flux within the last closed flux surface (Wb). + + Returns + ------- + f : ndarray + Quasi-symmetry flux function error at each node (T^4/m^2). + + """ + # this parses the inputs into a dictionary expected by ``desc.compute.compute`` + params = self._parse_args(*args, **kwargs) + + # here we get the physics quantities from ``desc.compute.compute`` + data = compute_fun( + self._data_keys, # quantities we want + params=params, # params from previous line + transforms=self._transforms, # transforms and profiles from self.build + profiles=self._profiles, + ) + # next we do any additional processing, such as combining things, + # averaging, etc. Here we just scale things by the quadrature weights from + # the grid to make things roughly independent of the grid resolution. + f = data["f_T"] * self.grid.weights + + # finally, we call ``self._shift_scale`` here to subtract out the target and + # apply weighing and normalizations. + return self._shift_scale(f) diff --git a/docs/dev_guide/transform_2.ipynb b/docs/dev_guide/transform_2.ipynb new file mode 100644 index 0000000000..ab985265b5 --- /dev/null +++ b/docs/dev_guide/transform_2.ipynb @@ -0,0 +1,207 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fbb1cce6-2f0a-49c7-a80a-212408ee20b0", + "metadata": { + "tags": [], + "toc-hr-collapsed": true + }, + "source": [ + "# `transform.py`" + ] + }, + { + "cell_type": "markdown", + "id": "6771767d-912b-46b7-a8fc-0a459e5fae50", + "metadata": {}, + "source": [ + "DESC is a [pseudo-spectral](https://en.wikipedia.org/wiki/Pseudo-spectral_method) code, where the dependent variables $R$, $Z$, $\\lambda$, as well as parameters such as the plasma boundary and profiles are represented by spectral basis functions.\n", + "These parameters are interpolated to a grid of collocation nodes in real space.\n", + "See the section on [basis functions](https://desc-docs.readthedocs.io/en/latest/notebooks/basis_grid.html#Basis-functions) for more information.\n", + "\n", + "Representing the parameters as a sum of spectral basis functions simplifies finding solutions to the relavant physics equations.\n", + "This is similar to how the Fourier transform reduces a complicated operation like differentiation in real space to multiplication by frequency in the frequency space.\n", + "In particular, and a more relavant example, seeking a solution to a partial differential equation as a linear combination of spectral basis functions reduces the PDE to a set of ordinary differential equations of the coefficients which compose that linear combination.\n", + "The resulting ODEs can then be solved numerically.\n", + "\n", + "Once it is known which combination of basis functions in the spectral space compose the relavant parameters, such as the plasma boundary etc., these functions in the spectral space need to be transformed back to real space to better understand their behavior in real space.\n", + "\n", + "The `Transform` class provides methods to transform between spectral and real space.\n", + "Each `Transform` object contains a spectral basis and a grid." + ] + }, + { + "cell_type": "markdown", + "id": "6dbcf2ef-2649-40df-9f44-c2df820ec260", + "metadata": { + "tags": [] + }, + "source": [ + "## `build()` and `transform(c)`\n", + "\n", + "The `build()` method builds the matrices for a particular grid which define the transformation from spectral to real space.\n", + "This is done by evaluating the basis at each point of the grid.\n", + "Generic examples of this type of transformation are the inverse Fourier transform and a change of basis matrix for fininte dimiensional vector spaces.\n", + "\n", + "The `transform(c)` method applies the resulting matrix to the given vector, $\\mathbf{c}$, which specify the coefficients of the basis associated with this `Transform` object.\n", + "This transforms the given vector of spectral coefficients to real space values.\n", + "\n", + "The matrices are computed for each derivative order specified when the `Transform` object was constructed.\n", + "The highest deriviative order at which to compute the transforms is specified by an array of three integers (one for each coordinate in $\\rho, \\theta, \\zeta$) given as the `derivs` argument.\n", + "\n", + "Define the transform matrix as $A_{(d\\rho,d\\theta,d\\zeta)}$ for the derivative of order ${(d\\rho,d\\theta,d\\zeta)}$ (where each are integers).\n", + "This matrix transforms a spectral basis evaluated on a certain grid with a given set of coefficients $\\mathbf{c}$ to real space values $x$.\n", + "\n", + "$$ A\\mathbf{c} = \\mathbf{x}$$\n", + "\n", + "- $\\mathbf{c}$ is a vector of length `Transform.basis.num_modes` (the number of modes in the basis)\n", + "- $\\mathbf{x}$ is a vector of length `Transform.grid.num_nodes` (the number of nodes in the grid)\n", + "- $A$ is a matrix of shape `(num_nodes,num_modes)`.\n", + "\n", + "As a simple example, if the basis is a Fourier series given by $f(\\zeta) = 2 + 4*cos(\\zeta)$, and the grid is $\\mathbf{\\zeta} =\\begin{bmatrix}0\\\\ \\pi\\end{bmatrix}$, then\n", + "\n", + "$$\\mathbf{c}=\\begin{bmatrix} 2\\\\ 4 \\end{bmatrix}$$\n", + "$$A_{(0, 0, 0)} = \\begin{bmatrix} 1 & cos(0)\\\\ 1& cos(\\pi) \\end{bmatrix}$$\n", + "$$A_{(0, 0, 0)}\\mathbf{c} = \\begin{bmatrix} 1& 1\\\\ 1& -1 \\end{bmatrix} \\begin{bmatrix} 2\\\\ 4 \\end{bmatrix} = \\begin{bmatrix} 6 \\\\ -2 \\end{bmatrix}$$" + ] + }, + { + "cell_type": "markdown", + "id": "6a8f43e3-9ee8-449b-b6ea-4672a966d914", + "metadata": { + "tags": [] + }, + "source": [ + "## `build_pinv()` and `fit(x)`" + ] + }, + { + "cell_type": "markdown", + "id": "261815bc-a394-4c11-a5c0-9dc127b37b25", + "metadata": {}, + "source": [ + "The `build_pinv` method builds the matrix which defines the [pseudoinverse (Moore–Penrose inverse)](https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse) transformation.\n", + "In particular, this is a transformation from real space values to coefficients of a spectral basis.\n", + "Generic examples of this type of transformation are the Fourier transform and a change of basis matrix for fininte dimiensional vector spaces.\n", + "\n", + "Any vector of values in real space can be represented as coefficients to some linear combination of a basis in spectral space.\n", + "However, the basis of a particular `Transform` may not be able to exactly represent a given vector of real space values.\n", + "In that case, the system $A \\mathbf{c} = \\mathbf{x}$ would be inconsistent.\n", + "\n", + "The `fit(x)` method applies $A^{\\dagger}$ to the vector $\\mathbf{x}$ of real space values.\n", + "This yields the coefficients that best allow the basis of a `Transform` object to approximate $\\mathbf{x}$ in spectral space.\n", + "The pseudo-inverse transform, $A^{\\dagger}$, applied to $\\mathbf{x}$ represents the least-squares solution for the unknown given by $\\mathbf{c}$ to the system $A \\mathbf{c} = \\mathbf{x}$.\n", + "\n", + "It is required from the least-squares solution, $A^{\\dagger} \\mathbf{x}$, that\n", + "\n", + "$$A^{\\dagger} \\mathbf{x} = \\min_{∀ \\mathbf{c}} \\lvert A \\mathbf{c} - \\mathbf{x} \\rvert \\; \\text{so that} \\; \\lvert A A^{\\dagger} \\mathbf{x} - \\mathbf{x}\\rvert \\; \\text{is minimized}$$\n", + "\n", + "For this to be true, $A A^{\\dagger}$ must be the orthogonal projection onto the image of the transformation $A$.\n", + "It follows that\n", + "\n", + "$$A A^{\\dagger} \\mathbf{x} - \\mathbf{x} ∈ (\\text{image}(A))^{\\perp} = \\text{kernel}(A^T)$$\n", + "\n", + "$$\n", + "\\begin{align*}\n", + " A^T (A A^{\\dagger} \\mathbf{x} - \\mathbf{x}) &= 0 \\\\\n", + " A^T A A^{\\dagger} \\mathbf{x} &= A^T \\mathbf{x} \\\\\n", + " A^{\\dagger} &= (A^T A)^{-1} A^{T} \\quad \\text{if} \\; A \\; \\text{is invertible}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "Equivalently, if $A = U S V^{T}$ is the singular value decomposition of the transform matrix $A$, then\n", + "\n", + "$$ A^{\\dagger} = V S^{+} U^{T}$$\n", + "\n", + "where the diagonal of $S^{+}$ has entries which are are the recipricols of the entries on the diagonal of $S$, except that any entries in the diagonal with $0$ for the singular value are kept as $0$.\n", + "(If there are no singular values corresponding to $0$, then $S^{+}=S^{-1} \\implies A^{\\dagger}=A^{-1}$, and hence $A^{-1}$ exists because there are no eigenvectors with eigenvalue $0^{2}$)." + ] + }, + { + "cell_type": "markdown", + "id": "c17469b1-9273-421b-8da2-23364a14c9d4", + "metadata": {}, + "source": [ + "## Transform build options\n", + "There are three different options from which the user can choose to build the transform matrix and its pseudoinverse." + ] + }, + { + "cell_type": "markdown", + "id": "a05153d1-b6d6-413e-aea9-f08d6cd1a627", + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, + "source": [ + "### Option 1: `direct1`\n", + "\n", + "With this option, the transformation matrix is computed by directly evaluating the basis functions on the given grid.\n", + "The computation of the pseudoinverse matrix as discussed above is outsourced to scipy's library.\n", + "This option can handle arbitrary grids and uses the full matrices for the transforms (i.e. you can still specify to throw out the less significant singular values in the singular value decomposition).\n", + "This makes `direct1` robust.\n", + "However, no simplifying assumptions are made, so it is likely to be the slowest.\n", + "\n", + "The relavant code for this option builds the matrices exactly as discussed above.\n", + "\n", + "To build the transform matrix for every combination of derivatives up to the given order:\n", + "```python\n", + "for d in self.derivatives:\n", + " self._matrices[\"direct1\"][d[0]][d[1]][d[2]] = self.basis.evaluate(\n", + " self.grid.nodes, d, unique=True\n", + " )\n", + "```\n", + "The `tranform(c)` method for a specified derivative combination:\n", + "```python\n", + "A = self.matrices[\"direct1\"][dr][dt][dz]\n", + "return jnp.matmul(A, c)\n", + "```\n", + "To build the pseudoinverse:\n", + "```python\n", + "self._matrices[\"pinv\"] = (\n", + " scipy.linalg.pinv(A, rcond=rcond) if A.size else np.zeros_like(A.T)\n", + ")\n", + "```\n", + "The `fit(x)` method:\n", + "```python\n", + "Ainv = self.matrices[\"pinv\"]\n", + "c = jnp.matmul(Ainv, x)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "28b219dd-d6d6-4a2f-afe2-7ba0778328b1", + "metadata": {}, + "source": [ + "### Option 2: `direct2` nad option 3: `fft`\n", + "Functions of the toroidal coordinate $\\zeta$ use Fourier series for their basis.\n", + "So a Fourier transform can be used to transform real space values to spectral space for the pseudoinverse matrix.\n", + "\n", + "`Todo: Figure out how fft algorithm is used.`" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "desc-env", + "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.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/index.rst b/docs/index.rst index d0ff30cfd8..ca34864ef5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -61,10 +61,39 @@ :maxdepth: 1 :caption: Developer guides - adding_compute_funs - adding_objectives - adding_optimizers - notebooks/dev_guide/grid.ipynb + dev_guide/adding_compute_funs.rst + dev_guide/adding_objectives.rst + dev_guide/adding_optimizers.rst + dev_guide/backend.rst + dev_guide/compute.rst + dev_guide/compute_notebook.ipynb + dev_guide/compute_2.ipynb + dev_guide/configuration_equilibrium.rst + dev_guide/grid.ipynb + dev_guide/objectives.rst + dev_guide/transform_2.ipynb + dev_guide/notebooks/backend.ipynb + dev_guide/notebooks/basis.ipynb + dev_guide/notebooks/coils.ipynb + dev_guide/notebooks/compute_notebook_2.ipynb + dev_guide/notebooks/configuration.ipynb + dev_guide/notebooks/derivatives.ipynb + dev_guide/notebooks/equilibrium.ipynb + dev_guide/notebooks/examples.ipynb + dev_guide/notebooks/geometry.ipynb + dev_guide/notebooks/grid.ipynb + dev_guide/notebooks/interpolate.ipynb + dev_guide/notebooks/io.ipynb + dev_guide/notebooks/magnetic_fields.ipynb + dev_guide/notebooks/objectives.ipynb + dev_guide/notebooks/optimization_objectives_constraints.ipynb + dev_guide/notebooks/optimize.ipynb + dev_guide/notebooks/perturbations.ipynb + dev_guide/notebooks/plotting.ipynb + dev_guide/notebooks/transform.ipynb + dev_guide/notebooks/utils.ipynb + dev_guide/notebooks/vmec_utils.ipynb + dev_guide/notebooks/vmec.ipynb