{ "cells": [ { "cell_type": "markdown", "id": "897c6a94-d44e-4b89-bcb2-56a913faaafb", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "# Quick Overview" ] }, { "cell_type": "markdown", "id": "fe30cac8-1a28-46e1-b886-8b10979c4098", "metadata": {}, "source": [ "This chapter gives a quick overview into how to use the package. Detailed explanations can be found in the [userguide](../userguide/index.rst).\n", "\n", "Start by importing the package:" ] }, { "cell_type": "code", "execution_count": 1, "id": "56d821ed-27db-46eb-9206-435ac5208f53", "metadata": { "tags": [] }, "outputs": [], "source": [ "import h5rdmtoolbox as h5tbx" ] }, { "cell_type": "markdown", "id": "a80c93e7-6d5f-4187-896e-f8223d0449c3", "metadata": {}, "source": [ "## Difference to `h5py` package\n", "\n", "The `h5RDMtoolbox` is built upon the `h5py` package. The base functionality is kept, but convenient features and interfaces are added.\n", "\n", "### Filename\n", "A filename must not be provided when creating a new file. If none is provided, a temporary file is created. Also, `hdf_filename` is provided as an additional property allowing to work with the filename even after the file has been closed *and* to work with `pathlib.Path` objects instead of strings:" ] }, { "cell_type": "code", "execution_count": 2, "id": "641d270b-6c02-4296-8ec7-34dc05af90df", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "'tmp0.hdf'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "with h5tbx.use(None):\n", " with h5tbx.File() as h5:\n", " pass\n", "h5.hdf_filename.name # equal to h5.filename but a pathlib.Path and exists also after the file is closed" ] }, { "cell_type": "markdown", "id": "fa37608c-aba1-4823-a8ec-b2e7897d559c", "metadata": {}, "source": [ "## Additional arguments\n", "\n", "Here and there, the toolbox allows one-liners, e.g. by creating attributes during dataset creation. In the following example the dataset is also marked as a dimension scale:" ] }, { "cell_type": "code", "execution_count": 3, "id": "23187c3c-bff8-4340-9f88-999d94e0325a", "metadata": {}, "outputs": [], "source": [ "with h5tbx.File() as h5:\n", " ds_time = h5.create_dataset(name='time',\n", " data=[0, 1, 2, 3],\n", " attrs=dict(units='s',\n", " long_name='measurement time'),\n", " make_scale=True)" ] }, { "cell_type": "markdown", "id": "4228862d-8150-40fb-af60-0d46625dcada", "metadata": {}, "source": [ "### Datasets/xarray interface\n", "\n", "Data access will not return `np.ndarray` but a `xr.DataArray` object. It is capable of storing attributes and coordinates (similar concept as HDF dimension scales). Find out about all possibilities this give on [xarray's documentation](https://xarray.pydata.org/).\n", "\n", "Let's create some sample data and see how this new return object can help:" ] }, { "cell_type": "code", "execution_count": 4, "id": "69936b5e-d89b-47e5-868c-2121f55b264f", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "time = np.linspace(0, np.pi/4, 21) # units [s]\n", "signal = np.sin(2*np.pi*3*time) # units [V], physical: [m/s]\n", "\n", "with h5tbx.File() as h5:\n", " vel_hdf_filename = h5.hdf_filename # store for later use\n", " \n", " ds_time = h5.create_dataset(name='time',\n", " data=time,\n", " attrs=dict(units='s',\n", " long_name='measurement time'),\n", " make_scale=True)\n", " \n", " ds_signal = h5.create_dataset(name='vel',\n", " data=signal,\n", " attrs=dict(units='m/s',\n", " long_name='air velocity in pipe'),\n", " attach_scale=ds_time)" ] }, { "cell_type": "markdown", "id": "650e7a94-ac37-4941-9b29-8343d3884e2c", "metadata": {}, "source": [ "Inspired by `xarray` the methods `sel` and `isel` are implemented:" ] }, { "cell_type": "code", "execution_count": 5, "id": "5fe0de69-c4b1-4acb-8155-70ef82bd6dca", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataArray 'vel' ()>\n",
       "0.7855\n",
       "Coordinates:\n",
       "    time     float64 0.7854\n",
       "Attributes:\n",
       "    long_name:  air velocity in pipe\n",
       "    units:      m/s
" ], "text/plain": [ "\n", "0.7855\n", "Coordinates:\n", " time float64 0.7854\n", "Attributes:\n", " long_name: air velocity in pipe\n", " units: m/s" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "with h5tbx.File(vel_hdf_filename) as h5:\n", " vel2 = h5['vel'].sel(time=2, method='nearest')\n", "vel2" ] }, { "cell_type": "markdown", "id": "9f5c5ae5-a0c7-4b7b-955f-7ba05246be0c", "metadata": {}, "source": [ "Another advantage is using the plotting util form `xarray`:" ] }, { "cell_type": "code", "execution_count": 6, "id": "269b486d-4adc-47ce-89ac-42ff6c2f3d41", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataArray 'vel' (time: 21)>\n",
       "0.0 0.6745 0.9959 0.7962 0.1797 -0.5308 ... -0.6615 0.01737 0.6872 0.9973 0.7855\n",
       "Coordinates:\n",
       "  * time     (time) float64 0.0 0.03927 0.07854 0.1178 ... 0.7069 0.7461 0.7854\n",
       "Attributes:\n",
       "    long_name:  air velocity in pipe\n",
       "    units:      m/s
" ], "text/plain": [ "\n", "0.0 0.6745 0.9959 0.7962 0.1797 -0.5308 ... -0.6615 0.01737 0.6872 0.9973 0.7855\n", "Coordinates:\n", " * time (time) float64 0.0 0.03927 0.07854 0.1178 ... 0.7069 0.7461 0.7854\n", "Attributes:\n", " long_name: air velocity in pipe\n", " units: m/s" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAksAAAGwCAYAAAC5ACFFAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACKVUlEQVR4nO3de1yTdf8/8Ne1ARuHMc5sHBQ8IqKCmoRZWqJS3R66vSstS/uVfbOsTLtL77sitbLMyur2tjvLtKzseNdtB9TwlIZQIioHURAFYQM5bRw32K7fH2MD4rSNbdcO7+fjsUe5XRvvS9n2vj6f9+f9YViWZUEIIYQQQnrF4zoAQgghhBB7RskSIYQQQkg/KFkihBBCCOkHJUuEEEIIIf2gZIkQQgghpB+ULBFCCCGE9IOSJUIIIYSQfrhxHYAz0Gq1qKiogEgkAsMwXIdDCCGEECOwLIuGhgaEhYWBx+t7/IiSJQuoqKhAZGQk12EQQgghxAxlZWWIiIjo83FKlixAJBIB0P1l+/r6chwNIYQQQoyhVCoRGRlp+B7vCyVLFqCfevP19aVkiRBCCHEwA5XQUIE3IYQQQkg/KFkihBBCCOkHJUuEEEIIIf2gZIkQQgghpB+ULBFCCCGE9IOSJUIIIYSQflCyRAghhBDSD0qWCCGEEEL6QckSIYQQQkg/qIM3sRiNlkVWSS2qGloRIhJiSnQA+DzaWJgQQpyds3/+O9TI0rFjxzB37lyEhYWBYRh89913Az7nyJEjmDhxIgQCAUaMGIFdu3b1OGbbtm2IioqCUChEYmIisrKyLB+8k0vLlWHaa4eweMdJPLk3B4t3nMS01w4hLVfGdWiEEEKsyBU+/x0qWWpqasKECROwbds2o44vKSnB7bffjptvvhk5OTlYtWoVHnroIezfv99wzBdffIHVq1cjNTUV2dnZmDBhAubMmYOqqiprnYbTScuVYcWebMgUrd3ulytasWJPtlO9YQghhHRylc9/hmVZlusgzMEwDP773/9iwYIFfR7z7LPP4scff0Rubq7hvkWLFqG+vh5paWkAgMTERFx33XX417/+BQDQarWIjIzE448/jrVr1/b6uiqVCiqVyvBn/a7FCoXC5TbS1WhZTHvtUI83ih4DQCIW4viztzjVkCwhhLg6Z/j8VyqVEIvFA35/O9TIkqkyMjKQnJzc7b45c+YgIyMDAKBWq3Hq1Klux/B4PCQnJxuO6c2mTZsgFosNt8jISOucgAPIKqnt840CACwAmaIVWSW1tguKEEKI1bnS579TJ0tyuRyhoaHd7gsNDYVSqURLSwuqq6uh0Wh6PUYul/f5uuvWrYNCoTDcysrKrBK/I6hq6PuNYs5xhBBCHIMrff7TajgzCAQCCAQCrsOwCyEioUWPI8QVOPvKIeIaXOnz36mTJYlEgsrKym73VVZWwtfXF56enuDz+eDz+b0eI5FIbBmqw5oSHQCpWAi5ohV9Fb9JxbovA0KIriB2/b78btMXUrEQqXNjkRIn5TAyQkyj//wfqGbJGT7/nXoaLikpCenp6d3uO3jwIJKSkgAAHh4emDRpUrdjtFot0tPTDceQ/vF5DFLnxvaZKAHA87fH0lUzIXCdlUPENfB5DB67eUSfj7MAUuc6x+e/QyVLjY2NyMnJQU5ODgBda4CcnByUlpYC0NUS3X///YbjH3nkEVy6dAnPPPMMzp8/j3//+9/48ssv8dRTTxmOWb16NXbs2IHdu3ejoKAAK1asQFNTEx544AGbnpsjS4mT4roo/x73698eFYoW2wZEiB3SaFms35ff64WF/r71+/Kh0TrkAmXioq7UNAEAPNx6phPxkWKnGS11qGm4P/74AzfffLPhz6tXrwYALF26FLt27YJMJjMkTgAQHR2NH3/8EU899RTefvttRERE4IMPPsCcOXMMx9x99924du0aXnjhBcjlcsTHxyMtLa1H0TfpW3WjCjll9QCAV+6Ig7fADSEiIS5VN+Kf/83F6/sLcXNMCIYH+3AbKCEcMmXlUNLwQNsFRoiZmlTt2Pu7boHT9nsmwkvghqqGVrS2afCPb88hp0yB/XlyzBnr+GUtDpUszZgxA/21heqtO/eMGTNw+vTpfl935cqVWLly5WDDc1lf/F6GNg2L+Eg/3JM41HD/9cMCkJYrx68Xq/H3r87gq0emOsVwLCHmcKWVQ8Q1/Pd0ORpa2xEV6IWbY0LA6/L5fqWmGf8+UozU7/MwdXggREJ3DiMdPIeahiP2R6Nl8enJKwCA+5OGdnuMYRi8unA8fARuyC6tx4fHL3ERIiGcy6tQ4OtTV4061hlWDhHnx7Isdv12GQCwdGpUt0QJAJ6YORJDA70gV7bijQMXOIjQsihZIoOSXlCJCkUrArw9cNu4nnPT4X6eeO72MQCALQcuoKiq0dYhEsIJrZbFL/mVWPR+Bm5/5zh+vVjd7/EMaOUocRzHi6pRVNUIH4Eb/jYposfjQnc+Xl4wDgCwO+MyTpfW2TpEi6JkiQzKJx2jSndNjoTQnd/rMXdfF4mbRgVD3a7F378+QwWsxKk1qdrxccZlzHzzKB76+A+cvFQLPo/BX8ZL8cyc0WDQufjhz5xl5RBxfrtOXAYA/G1SRJ9TbNNGBuGvCeFgWWDdt+fQptHaMELLcqiaJWJfLl1rxK8Xq8EwwL2JQ/o8jmEYvLZwHGa/eQynS+vxwa+X8H/Th9swUkKsr6K+BbszLuPzzFIoW9sBACKhG+6ZMgT3T41CuJ8nAGBYsHePPks+AjdsuXO806wcIs7tcnUTDhXqNptfOjWq32P/efsYHC6swnl5Az74tQQrZjjmZz8lS8Rsn2bqVh7ePDoEkQFe/R4rFXvi+b/E4plvzuKNgxcwc0wIRoSIbBEmIWYzptN2Tlk9Pjxegp/OyQyjplGBXnjghmj8bVIEvAXdP2ZT4qSYFStBVkktfjxXgT0nSxHh70mJEnEYuzMug2WBm0cHIzrIu99jA30E+OftsXj6qzPY+ssF3DZOgqGB/T/HHlGyRMzSotbgqz90S0bv+1Nhd1/unByBn3JlOFJ4DWu+OotvHkmCG59mgol96q/TdvKYUBzIr8SHx0tw6kpnLcb1wwLw4LRhuCUmpN/pND6PQdLwQMRIRPgssxTn5Q0or28xjD4RYq8aVe346g/dYoUHbog26jkLJ4bj2+yr+K24Bs99l4uP/98UMIxjTTfTNxUxy//OlEPZ2o4hAV6YPjLYqOcwDINX/zoeIqEbzpTVY8evJVaOkhDz9Ndp+5E92Zjycjoe/TQbp67UwZ3P4K8J4fjh8WnY+3ASZsWGGl135O/tgUlDdQ1d0wsqBziaEO59/UcZGlXtGB7sjRtHBhn1HIZh8PId4+DhxsOvF6vxXU65laO0PEqWiMlYlsXHGbrC7iXXD+mxZLQ/ErEQL/wlFgDw1sELuFjZYJUYCTGXMZ22a5vV8PN0w8qbR+DEs7fgzbvjERcuNuvnzRyja4D7S0GVeQETYiNaLYvdHZ/9y6ZGmTQ6FB3kjSdnjgQAbPyhAHVNaqvEaC2ULBGTnS6rR16FEh5uPNw5KdLk5/9tUgRuiQmBWqPFmq/OoN2BV0gQ5zNQp229txcl4Ok5oxHiO7i+SMkdydLJ4ho0qtoH9VqEWNPRi9dQUt0EkdANf53Ys13AQJbfOAyjQn1Q26TGyz8VWCFC66FkiZhsT8eVxdzxYfD39jD5+QzD4JU7xsFX6IazVxX4zzFqVknsh7EdtOtb2izy84YHeyMq0AtqjRbHL16zyGsSYg0fdbQLuHtyZI+FC8bwcONh01/Hg2GAr09dxW9F/fcesyeULBGT1DSq8MNZ3c7of+7YbQqJWIjUuWMBAG//chGFcpqOI/bB2A7aluq0zTCMYSruYD5NxRH7VFTViGMXroFhgPuTosx+nUlD/Q2tZv75XS5a2zQWitC6KFkiJvnyj6tQa7QYHyHGhEi/Qb3WXyeGY2bHdNzfv6bpOGIfpkQHQCoW9tk40hqdtmeOCQEAHC6soqatxC59nHEZADAzJhRDAvtvFTOQZ1JiECISoKS6CdsOF1kgOuujZIkYTaNl8WmmbgruvuvNH1XSYxgGr/yVpuOIfeHzGKTOje21wFufQFm60/Z1UQHwFbqhtkmNnDLH3haCOB9la5thb8P/d0PUoF/PV+iO9fN0MwvbjxTjggMs9KFkiRjtSGEVrta1QOzpjrkTwizymqG+Qqyfr3vTbP3lAs7LlRZ5XUIGIyVOir8mhPe4XyIWYvuSiRZvIOnO52HGaN3oEq2KI/bmy9/L0KzWYFSoD5KGB1rkNVPiJEgeE4p2LYt1356D1s5HVClZIkbr3Acuos994MyxID4cyWNC0aZh8fRXZxx6/yDiPGqbdUubl1w/BG8visfny6/H8WdvsVqnbf1U3C/51G+J2A+NtrNVzLKp0RZrJskwDDbMHwtvDz5OXanDZ1mlFnlda6FkiRjlSk0Tjl7QrdRZYoEpuK50q+PiIPZ0R265Eu8dKbbo6xNiqnaNFr+X1AIAFk8Zgvnx4UgaHmjVTW5njNJ1/b5Y1YjSmmar/RxCTHH4fBVKa5sh9nTHHb2Mtg5GmJ8nnp4zGgDw2s/nUak0biUqFyhZIkb5NLMULAtMHxVslX19QnyFhjnsdw5dRIGMpuMId86VK9Ck1kDs6Y4xEl+b/EyxlzumROmKxn+hbt7ETnz0m26nhUVTIuHpYbkZBb37k6IwIUKMBlU71u/Ls/jrWwolS2RArW0afNmxD9xg2gUMZH58GGbH0nQc4d7JS7pRpcToAJM61A+WYSqOkiViBy5UNuBEUQ14jGUW9fSGz9Mt9OHzGPx0Tm6309CULJEB7TtTgfrmNoT7eRqKUK2BYRi8dEcc/LzckVehxL8P03Qc4UbGpRoAwPXDLFPMaix9N++sklooWy3T9JIQc+367TIAYHasBBH+g2sX0J+xYWI8dKNuU94Xvs9Fkx12sqdkiQxoT0dh973XD7FqzQaga/Snn45799BF5FfQdByxrTaNFn9c1o0sWWrlj7GigrwxIsQH7VoWRwupmzfhTn2zGt9m69oFPGCBdgEDWTVzFCIDPFGhaMUbBy5Y/eeZipIl0q8zZfU4c1UBDz4Pd082fR84c8ybEIY5Y3VLSp/+6gzU7TQdR2zn7FUFmtUa+Hu5Y3SoyOY/Xz8Vl05TcYRDX/xehtY2LcZIfS3agLUvnh58vLRgHABg128lOHu13uo/0xSULJF+6dsF3D5eikAfgU1+JsMweGnBOPh7uSNfpnSYDq/EOZzsmIJLjA60ab2Snn4q7nDhNepqTzjRrtEa2gU8MDXKYu0CBjJ9VDDmx4dBywJrvzlnV7//lCyRPtU1qbHvTAUA4D4rFnb3JlgkwIb5cQCAbYeLcPZqPTKKa/B9TjkyimtoSwhiNfpkydZTcHoTh/jD38sdipY2/HGFunkT2/uloArl9S0I8PbAvHjLNCA21vN/iYXYU3ehvPNEiU1/dn8oWSJ9+upUGVTtWowN80XCIPeBM8dfxktxa5wE7VoWf/33b1i84ySe3JuDxTtOYtprh5CWK7N5TMS5qdu1+OOyLkGxdXG3Hp/H4OYYmooj3PmoI0lZPCXSog2IjRHkI8A/bxsDAHjr4EWU1dpHzzFKlkivtFoWe07qOqred/1Qmw3DdsUwnV8a7X8aSZIrWrFiTzYlTMSizl6tR0ubBgHeHhgV6sNZHPqpuHTa+oTYWH6FEpklteDzGIs3IDbWnZMjcP2wALS0afDP/55DRnE157MKbpz8VGL3jl68htLaZoiEbpgfb9murcbSaFm8dbD3VREsdJuart+Xj1mxEquv0iOuIaNY3zIggJMLBL0bRwbBnc/gUnUTiq81Yngwd4kbcS27O9oF3BongVTsyUkMDMPg5TvGYc5bx3DsYjWOXaw2PCYVC5E6N9Zq2w71hUaWSK/2dBT33TnJOl1bjZFVUguZou/29ywAmaIVWR3bUhAyWCdLOuqVOJqC0xMJ3Q3TgDQVR2yltkmN73LKAdimXUB/LlY29JhRALibVaBkifRQVtuMQ4W64f8l1w/hLI6qBuP2CTL2OEL6o2rXGOqVuCru7ko/FfcLTcURG/k8qxSqdi3GhYsxcYg/Z3FotCzW78vv9TF9+rR+X75Np+QoWSI96PeBu3FkEIZxOPwfIhJa9DhC+nOmTAFVuxZBPgK7mPbS91v643It6prUHEdDnF2bRmtoQLzMhu0CemOPswqULJFuWts0+OJ3XWE3V8V9elOiAyAVC9HXW5aBbv7aFg3TiPOzl3olvQh/L8RIRNCywJELNLpErGt/nhwyRSuCfDzwlwm2rQf6M3ucVaBkiXTz0zkZ6prbECYWYmaM9faBMwafxyB1biwA9EiY9H9OnRtLxd3EIjIu6YpIuWoZ0JvOjXUpWSLWtevEZQDAPYlDIXDjpk5Vzx5nFRwuWdq2bRuioqIgFAqRmJiIrKysPo+dMWMGGIbpcbv99tsNxyxbtqzH4ykpKbY4Fbuk79p6T+IQuPG5//VIiZNi+5KJkIi7vykkYiG2L5lo8xURxDm1tmmQXVoPwD7qlfT0dUvHCq/Rtj/Eas5dVeCPK3Vw5zNYkshdnaqePc4qcP9taIIvvvgCq1evRmpqKrKzszFhwgTMmTMHVVW9X3V9++23kMlkhltubi74fD7uvPPObselpKR0O+7zzz+3xenYnXNXFcgpq4c7n8Hd13H/htFLiZPi+LO3YPf/uw76QaS9D19PiRKxmNOl9VC3axEsEmBYkDfX4RhMiPBDkI8ADap2WvVJrOaj33RNKG8fJ0WIL/c1oPY4q+BQydKbb76J5cuX44EHHkBsbCzee+89eHl5YefOnb0eHxAQAIlEYrgdPHgQXl5ePZIlgUDQ7Th/f+5WAXBJX9x3a5wUwSLb7ANnLD6PwfRRIYgLFwMAzpUrOI6IOBPDFifDAu2iXkmPx2NwS0wwAOAXaiFArOBagwo/nNEtw192QzTH0XSyt1kFh2lKqVarcerUKaxbt85wH4/HQ3JyMjIyMox6jQ8//BCLFi2Ct3f3K8cjR44gJCQE/v7+uOWWW/DSSy8hMLDvoXiVSgWVSmX4s1KpNPFs7I+iuQ3fn9H117D1PnCmiI/0w9mrCpwurcdfxtt2zyLivDIu6Yu77WcKTi95TCi+/OMq0s9XInVurF0lc8TxfZ5VCrVGi/hIP8RzsK1Vf1LipJgVK0FWSS2qGloRItJNvXFRp+owI0vV1dXQaDQIDQ3tdn9oaCjkcvmAz8/KykJubi4eeuihbvenpKTg448/Rnp6Ol577TUcPXoUt956KzQaTZ+vtWnTJojFYsMtMjLSvJOyI1+dKkNrmxYxEhEmD7XfkbWEIX4AgJyyek7jIM6jtU2DHDusV9KbNjIIHm48lNW24GJVI9fhECeibu9sF8B1E8q+8HkMkoYHYn58OJKGB3K2oMdhkqXB+vDDDzFu3DhMmTKl2/2LFi3CvHnzMG7cOCxYsAA//PADfv/9dxw5cqTP11q3bh0UCoXhVlZWZuXorUu3D5zuDXNfEjf7wBkrPlKXyJ0rV1DBK7GI7Ct1UGu0kPgKERXoxXU4PXh5uOGGjiTuYD5NxRHL+TlXhqoGFUJEAtxKNaD9cphkKSgoCHw+H5WV3T8sKisrIZFI+n1uU1MT9u7diwcffHDAnzNs2DAEBQWhqKioz2MEAgF8fX273RzZ8aJqXK5phkjghgUc7QNnrKhAL/h5uUPdrsV5ueNPfxLudU7B2Ud/pd7MNGysS8kSsZyPOtoFLLl+KDzcHCYd4ITD/O14eHhg0qRJSE9PN9yn1WqRnp6OpKSkfp/71VdfQaVSYcmSJQP+nKtXr6KmpgZSqetk2Z90jCotnBQBb4F9l7ExDGOYVz/dMXVCyGAYirvtcApOT99v6XRZPaobVQMcTcjATpfWIaesHh58HhZPsZ/Vz/bKYZIlAFi9ejV27NiB3bt3o6CgACtWrEBTUxMeeOABAMD999/frQBc78MPP8SCBQt6FG03Njbi73//O06ePInLly8jPT0d8+fPx4gRIzBnzhybnBPXyutbDFerXO4DZ4qEjqk4qlsig9Wi1hh+j+yxuFtPKvZEXLgvWBY4dJ4aVJLB2/XbZQDA3Alhdrf62R7Z9zDCn9x99924du0aXnjhBcjlcsTHxyMtLc1Q9F1aWgoer3v+V1hYiOPHj+PAgQM9Xo/P5+Ps2bPYvXs36uvrERYWhtmzZ2Pjxo0QCFzjl+ezzCvQssDU4YEYESLiOhyjxHcUeZ8ureM2EOLwTl2pQ5uGRZhYiCEB9lev1NXMmFDkliuRXlCJuyY7/qISYnsaLYusklpcrGrAvjMVAHT7wJGBOVSyBAArV67EypUre32st6Ls0aNHg2V735nY09MT+/fvt2R4DkXVrsHeLF1x+n0c7wNnivgIPwDA5Zpm1DWp4e/twW1AxGF13eLEXuuV9JLHhOLt9Iv49WI1Wts0ELpzuyUFcSxpuTKs35ffbYNadz6D8vpmjIsQcxiZY3CoaThiGRoti4ziGrz8YwFqmtQIEXlgVmzowE+0E2IvdwwL1vXKoqk4MhgnL+m6Yl9vx/VKenHhvgj1FaBZrTHUWRFijLRcGVbsye6WKAFAm4bFij3ZSMuVcRSZ46BkycWk5cow7bVDWLzjpGEfuGa11uG6A+vrlk5TskTM1KRqx5mO358kO65X0mMYxrAqztHer4Q7Gi2L9fvy0fv8is76ffnQaPs7glCy5EL6urpoVLU73NUF1S2RwTp1pQ7tWhbhfp6ItPN6Jb3kjlVxhwqq+iwvIKSrrJLaHp/5XbEAZIpW2ntwAJQsuQhnu7pI6GgfcKasHloHiZnYlwwHaBnwZ1OHB0HozkOFohX5MuozRgZW1dB3omTOca6KkiUX4WxXFzESEYTuPChb23GpuonrcIgDyii23/3g+iJ05+PGkR0b6+ZTCwEysBCRcOCDTDjOVVGy5CKc7erCjc/D+HA/AFTkTUzXqGrHuXIFAF3nbkein4pLP091S2RgU6IDIBUL0ddaTwaAVKzboJb0jZIlF+GMVxdUt0TM9fvlWmi0LCIDPBHh7xj1Sno3x+iSpbNXFahUOsbFDeEOn8cgdW5sr4/pE6jUubGcbVDrKChZchHOeHWh3/aERpaIqQxbnDjQFJxeiEho+N2nbt7EGClxUmxfMhF/zockYiG2L5mIFNpEd0CULLkIZ7y6SOgYWTovb0CLWsNtMMShnCx2vOLurvRTcb/k01QcMc7EIf7QsrrP+9f/Nh6fL78ex5+9hRIlI1Gy5EJS4qTYMD+ux/2OenUhFXsi1FcAjZY11J8QMpCG1rYu9UqOmSzp+y0dL6qmCwViFP3v/IgQH9w5ORJJwwMd6uKYaw633QkZnICOrUGig7yxKnkkQkS6qTdHfdMkRPojLU+O06V1DjWFSLjz++VaaFkgKtALUrEn1+GYJUYiQrifJ8rrW3CiqBrJDtSBn3BDnyyNC6etTcxBI0supusV9fz4cIe/utAXeVPdEjGWI7YM+DOGYWhVHDFJbsdnfxwlS2ahZMnF5FU419WFvjnl6dJ6TuMgjkO/H5yj1ivpdW59UkWNWcmA9BfK42nTXLNQsuRCWJZ1uqHYcRFi8HkM5MpWyBQtXIdD7Jyipc1wweDII0sAkDgsAN4efFxrUFHNHulXVUMrKpUq8BggNsyX63AcEiVLLuRqXQvqm9vgzmcwSuLDdTgW4eXhhtGhIgBADo0ukQH8XqKrVxoW5I1QX8fpKdYbgRsfN43SdfNOp411ST/0U3DDg33g5UGlyuagZMmF6N8wo0JFELjxOY7GcqhuiRhLvx9cooOPKukld0zFHSygfkukb+eu6vYRdJYZBS5QsuRCcp2sXkmP6paIsU464Oa5/bk5JgQ8BiiQKVFeT9PQpHfnqLh70ChZciHnynVXF2Od7A2jb055trwe7Rott8EQu1XfrEa+TPcecLT94PoS4O2BiUP8AQCHaCqO9EE/qzCOirvNRsmSi2BZtvMN42TJ0rAgH4iEbmht0+K8vIHrcIidyiypBcsCw4O9HWoPxIF0XRVHyJ9da1BBrmwFwwCxUiruNhclSy5CpmhFbZMabjwGMRIR1+FYFI/H0D5xZEDONgWnNytW128po7gGjap2jqMh9qZrcbe3gIq7zUXJkovQz1mPDBVB6O48xd16VLdEBuIMzSh7MzzYB0MDvaDWaHH84jWuwyF2xtnaxXCFkiUXYeje6qQ9NjpXxNVxGwixS3VNasMUrbMlSwzDYGYMTcWR3lFxt2VQsuQizjl5gV98pK7ItfhaExTNbRxHQ+xNZoluVGlkiA+CfAQcR2N5yR1TcYfPV0FD3bxJF+eu0siSJVCy5AK6Fnc769VFgLcHhgZ6AQDOXK3nNhhid5xli5O+XBcVAJHQDTVNahpdJQZdi7vHOumsgq1QsuQCKpUqVDeqwecxTr0aguqWSF/09UpJTjYFp+fO52HGaN3oEk3FET39RfKwIG8q7h4kSpZcgH4KbkSwj1MWd+t1roijK2vSqaZRhcJKXb2Ss3Tu7k3yGF2yRFufED0q7rYcSpZcgKsU+CV0NOfLKasHy1LdBtHJLNFNwcVIRAjw9uA4GuuZMSoEfB6DC5WNKK1p5jocYgc6a1X9uA3ECVCy5ALyDFcXzjsFBwBjpL7wcOOhrrkNV+jLgnRw1pYBfyb2csd1UboLhg9+vYTvc8qRUVxDBd8uzFkbEXOBJjFdgKuMLHm48RAX5ovs0nqcLqtDVJA31yERO6BvRunsyRIARPh5AgA+PnkFH5+8AgCQioVInRuLlDgpl6ERG6tuVEGmoOJuS6GRJSdXpWxFVYMKPAaIdYE3jL6FABV5E0C3GuhiVSMYBkiMdo794PqSlivD19nlPe6XK1qxYk820nJlHERFuHKOirstipIlJ5db0dnq3svD+d8wCYbmlPWcxkHsg76/UozEF/5OXK+k0bJYvy+/18f0k3Dr9+XTlJwLyaX+ShblcMnStm3bEBUVBaFQiMTERGRlZfV57K5du8AwTLebUNh9A02WZfHCCy9AKpXC09MTycnJuHjxorVPw2bOXdXtsu4qbxj9irj8CiVa2zTcBkM45+wtA/SySmohU7T2+TgL3f6QWR3F7sT5uUr5ha04VLL0xRdfYPXq1UhNTUV2djYmTJiAOXPmoKqq774ivr6+kMlkhtuVK1e6Pb5582a88847eO+995CZmQlvb2/MmTMHra19f/A4Ev0bZqyLvGEi/D0R5CNAu5ZFXseoGnFdnfVKzj0FV9Vg3OeVsccRx0fF3ZblUMnSm2++ieXLl+OBBx5AbGws3nvvPXh5eWHnzp19PodhGEgkEsMtNDTU8BjLsti6dSuee+45zJ8/H+PHj8fHH3+MiooKfPfddzY4I+tztTcMwzCG0SWqW3JtVcpWFF9r6qhXcu6RpRCRcOCDTDiOOLaaRhUq9MXdLvLZb20Okyyp1WqcOnUKycnJhvt4PB6Sk5ORkZHR5/MaGxsxdOhQREZGYv78+cjLyzM8VlJSArlc3u01xWIxEhMT+31NlUoFpVLZ7WaPura6d4Xibj193dJpqltyaRkdo0qxUl+Ivdw5jsa6pkQHQCoWgunjcQa6VXFTnLzInejoZxSig7zhQ8XdFuEwyVJ1dTU0Gk23kSEACA0NhVwu7/U5o0ePxs6dO/H9999jz5490Gq1mDp1Kq5evQoAhueZ8poAsGnTJojFYsMtMjJyMKdmNfribld7w+i3PcmhkSWXZtgPzsnrlQCAz2OQOjcWAHokTPo/p86NBZ/XVzpFnAltnmt5DpMsmSMpKQn3338/4uPjMX36dHz77bcIDg7Gf/7zn0G97rp166BQKAy3srIyC0VsWa66GmJ8pB8YBiivb0GVkmo0XJUr9VcCgJQ4KbYvmQiJuPtUm0QsxPYlE6nPkguhbU4sz6jhhoAA04ZuGYZBdnY2hg4dalZQvQkKCgKfz0dlZfd9jyorKyGRSIx6DXd3dyQkJKCoqAgADM+rrKyEVNr5QVJZWYn4+Pg+X0cgEEAgEJh4Brbnqm8YH4EbRoWIUFjZgNNl9Zgz1rjfD+I85IpWlFQ3gccA17nQ1FNKnBSzYiXYdrgIbx68gKhAL6SvmUEjSi4ml1bCWZxRyVJ9fT22bt0KsXjgv3iWZfHoo49Co7Hssm0PDw9MmjQJ6enpWLBgAQBAq9UiPT0dK1euNOo1NBoNzp07h9tuuw0AEB0dDYlEgvT0dENypFQqkZmZiRUrVlg0fi7kVehqqVzxDZMwxA+FlQ3IoWTJJelHlcaGiSH2dO56pT/j8xjckRCONw9eQEV9KzRalpIlF6Iv7gaoc7clGV3IsmjRIoSEhBh17OOPP252QP1ZvXo1li5dismTJ2PKlCnYunUrmpqa8MADDwAA7r//foSHh2PTpk0AgA0bNuD666/HiBEjUF9fj9dffx1XrlzBQw89BEA3ArZq1Sq89NJLGDlyJKKjo/H8888jLCzMkJA5qtomNcrrWwC4VnG3XnykH/b+XobTpXVch0I4oE+Wkoa7xhTcn0X4e0IkdENDazuKrzVijNT1PgNclaFzd7A3RELXulCwJqOSJa1Wa9KLNjQ0mBXMQO6++25cu3YNL7zwAuRyOeLj45GWlmYo0C4tLQWP11mGVVdXh+XLl0Mul8Pf3x+TJk3Cb7/9htjYWMMxzzzzDJqamvDwww+jvr4e06ZNQ1paWo/mlY6m62oIXxd8wyQM0W17cvaqgq6sXZB+JZwrFHf3hmEYxEp9kVlSi/wKJSVLLsTV2sXYisMtkVq5cmWf025Hjhzp9ue33noLb731Vr+vxzAMNmzYgA0bNlgqRLvg6nPWI0J84O3BR5NagwuVDfRl4UIq6ltwpaYZfB6DyVH+XIfDmdiwjmRJpsRCroMhNuOqtarWZvJquN27d+PHH380/PmZZ56Bn58fpk6d2qM7NuGOIVlywSk4QFe3MUHfQoD6LbkU/RRcXLjYpachYjsuEPIr7LMPHLGO3HLXrVW1JpOTpVdeeQWenp4AgIyMDGzbtg2bN29GUFAQnnrqKYsHSMxDVxfo0smb6pZciX4/OGff4mQg+lrFfJkSLEsb6LqCrrWqVNxtWSZPw5WVlWHEiBEAgO+++w4LFy7Eww8/jBtuuAEzZsywdHzEDHVNalyt63jDuHCypK9bopEl13KyxLXrlfRGhojgzmegaGlDhaIV4X6eXIdErMxQ3B1Exd2WZvLIko+PD2pqdB9GBw4cwKxZswAAQqEQLS0tlo2OmEXfMmBooJfLLZvuSj+ydLGqEQ2tbdwGQ2zial0zympbOuqVXHtkycONhxEhIgA0FecqXL1W1ZpMTpZmzZqFhx56CA899BAuXLhg6FmUl5eHqKgoS8dHzHDOUK/k2m+YYJEAEf6eYFndqjji/PRbnIyPELvUFj99obol10LbnFiPycnStm3bkJSUhGvXruGbb75BYKBuqPvUqVNYvHixxQMkpqOri05Ut+Ra9PVKrj4Fp9dZt0QXC67gHH32W43Rl147d+7EvHnzEBQUhH/96189Hl+/fr1FAyPmo+LuTglD/PHDWRnVLbkIV9sPbiCGkSUZjSw5u27F3eFU3G1pRo8s7dmzBxEREZg6dSpee+01nD9/3ppxETMpmttQWtsMAIijN0yXkaV6WhHk5Mpqm1Fe3wI3F++v1JU+WSqrbYGiher2nJmrNyK2NqOTpUOHDkEmk+HRRx/FqVOnMGXKFIwcORJr1qzBsWPHTO7yTawjr0L3honw94SflwfH0XBvbJgv3PkMarqsECTOST8FNyHSD14eVK8EAGIvd8MquPM0uuTUqPzCukyqWfL398eSJUvw5Zdforq6Gu+++y5aWlpw7733IiQkBPfffz++/vprNDU1WSteMgCagutO6M43XF1nU92SUzvp4luc9KVrvyXivDqLu2lGwRpMLvDW8/DwQEpKCv7973+jrKwMaWlpiIqKwsaNG/Hmm29aMkZigtwK6t76Z9RvyblptCwyiqtx6HwVAGCKi7cM+DNaEecaqLjbuiw2Vj158mRMnjwZGzZsQFsbzY1zhYZie+pat0ScS1quDOv35UOmaDXc98w3Z/DivLFIiZNyGJn9oJEl51fXpbibPvutw+RkiWVZfP311zh8+DCqqqq61SoxDINvvvkG7u5UXMYFZWsbSqp1U6A0DdcpYYgfAN2VtapdA4Ebn9uAiEWk5cqwYk82/ly2X6lUYcWebGxfMpESJnSOLF2sbIS6XQsPN7MnFIidouJu6zP5XbNq1Srcd999KCkpgY+PD8RiseHm60tzpVzK69hAMdzPEwHeVNytNyTACwHeHlBrtDQV4SQ0Whbr9+X3SJQAGO5bvy8fGi2tgIzw94RI6Aa1Rovia41ch0OsgKbgrM/kkaVPPvkE3377raFzN7Ef+pVw1DKgO4ZhEB/ph0Pnq5BTVm+oYSKOK6ukttvU25+xAGSKVmSV1CJpuGsXfDMMg1ipLzJLapFfocQYKX0+OJvccirutjaTR5bEYjGGDRtmjVjIINE2J32juiXnUtXQd6JkznHOjuqWnBuNLFmfycnSiy++iPXr19OmuXbI8IaJoDfMn+nrlmhFnHMIEQktepyzoxVxzquuSw85Spasx+RpuLvuuguff/45QkJCEBUV1aOYOzs722LBEeM1qtoNxd00stTT+Ag/AEBpbTNqGlUI9BFwGxAZlCnRAZCKhZArWnutW2IASMRCTImmNgJA95EllmXBMAzHERFLye0ov4gK9KLibisyOVlaunQpTp06hSVLliA0NJTedHYiv0IJlgUkvkIEiygR+DOxpztGhPigqKoROWX1mDkmlOuQyCDweQxS58ZixZ6eF2f6T6TUubHg8+jzCQBGhojgzmegaGlDhaLV0NWbOD6agrMNk5OlH3/8Efv378e0adOsEQ8xE71hBhYf6YeiqkacLqVkyRmkxEmxfclErPzsNNq7rHqTiIVInRtLbQO68HDjYUSICAUyJfIrlJQsOZHOzt302W9NJidLkZGR1CLADuXSNicDShjih69PXaW6JSdyc0yIYYPkF+fFYnSoL6ZEB9CIUi9ipb6GZGlWLF0sOAva4so2TC7wfuONN/DMM8/g8uXLVgiHmMuQLEVQItsX/Yq4M2X10FL/HadwsbIRGhbw83LH0qQoJA0PpESpD511SwqOIyGW0rW4eywlS1Zl8sjSkiVL0NzcjOHDh8PLy6tHgXdtba3FgiPGaVa3G5rNUXF330aHiuDpzkeDSvf3NTJUxHVIZJD0S+HHSHypfnIAhhVx1D7AaeiLu4cGekHsScXd1mRysrR161YrhEEGI79CCS0LhIgECPGlpdJ9cePzMC5CjKySWpwuradkyQkU6JMlarQ4IH2yVFbbAkVLG325OgGqVbUds1bDEftCc9bGSxjip0uWyupx13WRXIdDBqkzWaLEdyBiL3eE+3mivL4F52VKJA5z7c7mzkBffjGePvutzqiaJaXStGHbhoYGs4Ih5snt2BOOri4GlmDo5F3HbSBk0FiWNTRZ1NfjkP5RJ2/nQhfKtmNUsuTv74+qqiqjXzQ8PByXLl0yOyhimlwaijWafl+4C5UNaFK1cxwNGYwKRSuUre1w4zEYEeLDdTgOgTp5O4/6ZjXKaqm421aMmoZjWRYffPABfHyM+0Bqa2sbVFDEeC1qDS5W6Uby6OpiYKG+QkjFQsgUrTh7VeHym6w6soKOL/wRIT4QuPE5jsYx0MiS89DPKFBxt20YlSwNGTIEO3bsMPpFJRJJj1VyxDryZbri7iAfAUJ9qXO3MRKG+EF2To6csnpKlhyYvl4ploq7jab/u7pY2Qh1uxYebiZ3jyF2goq7bcuoZIl6KtmvvAr9G4aWThsrPtIPP52TU92Sg8unlXAmi/D3hEjohoZWXfsM+rtzXNSI2LbossLBUat70+nrlk6X1Ru6PxPHQ20DTMcwDNUtOQkq7rYth0uWtm3bhqioKAiFQiQmJiIrK6vPY3fs2IEbb7wR/v7+8Pf3R3Jyco/jly1bBoZhut1SUlKsfRoWQ0OxposLE4PPY3CtQYUKRSvX4RAzNKracaW2GQC1DTAV1S05PkVzG0o7fv+pEbFtOFSy9MUXX2D16tVITU1FdnY2JkyYgDlz5vS5Uu/IkSNYvHgxDh8+jIyMDERGRmL27NkoLy/vdlxKSgpkMpnh9vnnn9vidAattU2Di1W6zt10dWE8Tw++4Qs2p7Se22CIWQrlSrAsEOorQKAP1eqZgkaWHJ/+InlIgBfEXlQfbAsOlSy9+eabWL58OR544AHExsbivffeg5eXF3bu3Nnr8Z9++ikeffRRxMfHIyYmBh988AG0Wi3S09O7HScQCCCRSAw3f39/W5zOoJ2XN0CjZRHg7QGpmDp3myKe+i05tHyZbgUoTcGZruvIEk1DOyaagrM9h0mW1Go1Tp06heTkZMN9PB4PycnJyMjIMOo1mpub0dbWhoCAgG73HzlyBCEhIRg9ejRWrFiBmpqafl9HpVJBqVR2u3Gh6xQcFXebJiFSlxDnlNVzGwgxC9UrmW9kiAjufAaKljaahnZQ1FvP9sxKln799VcsWbIESUlJhimtTz75BMePH7docF1VV1dDo9EgNDS02/2hoaGQy+VGvcazzz6LsLCwbglXSkoKPv74Y6Snp+O1117D0aNHceutt0Kj0fT5Ops2bYJYLDbcIiO52TYj11DcTV8Ypoof4gdAl3C2abTcBkNMRm0DzOfhxsOIEN00NE3FOSYaWbI9k5Olb775BnPmzIGnpydOnz4NlUoFAFAoFHjllVcsHqClvPrqq9i7dy/++9//QijsnLJatGgR5s2bh3HjxmHBggX44Ycf8Pvvv+PIkSN9vta6deugUCgMt7KyMhucQU/6HaepwM900YHeEHu6Q9WuxXkZbc/jSDRa1vBvRiNL5qG6JcfVrbibLpRtxuRk6aWXXsJ7772HHTt2dGs8ecMNNyA7O9uiwXUVFBQEPp+PysrKbvdXVlZCIpH0+9wtW7bg1VdfxYEDBzB+/Ph+jx02bBiCgoJQVFTU5zECgQC+vr7dbramatfgQqXuC4OGYk3H4zGYoK9bKqO6JUdypaYJLW0aCN15iA7y5joch9RZt6TgOBJiKv1FcmSAJ/y8PDiOxnWYnCwVFhbipptu6nG/WCxGfX29JWLqlYeHByZNmtStOFtfrJ2UlNTn8zZv3oyNGzciLS0NkydPHvDnXL16FTU1NZBKpRaJ21oK5Q1o07Dw83JHhL8n1+E4JP2murQizrEUdIwqjQ4Vgc+jWj1zGEaWqH2Aw9FPwY0P9+M2EBdjcrIkkUh6HXU5fvw4hg0bZpGg+rJ69Wrs2LEDu3fvRkFBAVasWIGmpiY88MADAID7778f69atMxz/2muv4fnnn8fOnTsRFRUFuVwOuVyOxkbdcvvGxkb8/e9/x8mTJ3H58mWkp6dj/vz5GDFiBObMmWPVcxmsrnPWVNxtHn3d0mkq8nYo+tEQ/egIMZ0+WSqrbYGihfbydCTUW48bJidLy5cvx5NPPonMzEwwDIOKigp8+umnePrpp7FixQprxGhw9913Y8uWLXjhhRcQHx+PnJwcpKWlGYq+S0tLIZPJDMdv374darUaf/vb3yCVSg23LVu2AAD4fD7Onj2LefPmYdSoUXjwwQcxadIk/PrrrxAI7Lt3i34TxbFUr2S2+Ag/AEBJdRPqmtTcBkOMVkD1SoMm9nJHuJ9uRPo8jS45FNrmhBtG7Q3X1dq1a6HVajFz5kw0NzfjpptugkAgwNNPP43HH3/cGjF2s3LlSqxcubLXx/5clD3Qnnaenp7Yv3+/hSKzLXrDDJ6/tweig7xRUt2EnKv1uHl0CNchESNQ2wDLiA3zRXl9C/JlSiQOow2lHYGipQ1Xaqi4mwsmjywxDIN//vOfqK2tRW5uLk6ePIlr165h48aN1oiP9ELdrkWhXHd1TcnS4MRH6P7+vsgqQ0ZxDTRaatJnz+qa1JB19AaKkdA2J4NBK+IcT145FXdzxeSRJT0PDw+IRCKIRCL4+PhYMiYygAuVDVBrtPAVuiEygIq7zZWWK0P6ed1WOWl5cqTlySEVC5E6NxYpcfZd4O+q9KNKQwK8IBLSNg+DQXvEOR7qr8Qdk0eW2tvb8fzzz0MsFiMqKgpRUVEQi8V47rnn0NZGhYK2kEuduwctLVeGFXuyoWxt73a/XNGKFXuykZYr6+OZhEv5hik4GlUaLP3I0sXKRqjbqTGrI6Dibu6YPLL0+OOP49tvv8XmzZsNS/YzMjLw4osvoqamBtu3b7d4kKQ7uroYHI2Wxfp9+ehtwo0FwABYvy8fs2IltDTdzlBxt+VE+HtCJHRDQ2s7iq810t+pA6BaVe6YnCx99tln2Lt3L2699VbDfePHj0dkZCQWL15MyZIN0L5Ag5NVUmuoe+kNC0CmaEVWSS2ShlPhqz3Jp21OLIZhGMRKfZFZUov8CiUlS3ZO0dKGy/ribloFbXMmT8MJBAJERUX1uD86OhoeHlRwZm1tGi0K5NS5ezCqGozbPNTY44htqNu1KKqikSVLorolx6Ev7o7w94S/N33X2prJydLKlSuxceNGw55wAKBSqfDyyy/3uaSfWI6+vkAkcMPQAC+uw3FIISLhwAeZcByxjeJrjWjTsBAJ3ahrvYXQijjHQeUX3DJ5Gu706dNIT09HREQEJkyYAAA4c+YM1Go1Zs6cib/+9a+GY7/99lvLRUoAdE7BjQ33BY/qacwyJToAUrEQckVrr3VLDACJWIgp0QG2Do30Q/+FPkbqSwsbLKTryBLLsvT3aseouJtbJidLfn5+WLhwYbf7IiMjLRYQ6Z9+E0W6ujAfn8cgdW4sVuzJBgP0mjClzo2l4m47U0D1ShY3MkQEdz4DRUsbKhSthq7exP5QcTe3TE6WPvroI2vEQYxEVxeWkRInxfYlE7F+X363Ym9/L3ds+us46rNkhwrk1DbA0jzceBgRIkKBTIn8CiUlS3ZK2dpZ3E3JEjdMrlki3GnXaA1X15QsDV5KnBTHn70Fny+/3jDltuT6oZQo2SGWZaltgJVQ3ZL9y6Xibs4ZNbI0ceJEpKenw9/fHwkJCf3Oa2dnZ1ssONJd0bVGtLZp4e3BR3SgN9fhOAU+j0HS8EAUyCTIKqnF+Y6VhsS+VCpVqG1Sg89jMCqURpYsKTbMF99kA/kyBdehkD7QFBz3jEqW5s+fD4FAAABYsGCBNeMh/cgt1135jQ0TU3G3hRkKXenq2i7pR1SHBXlD6M7nOBrnYhhZovYBdutcOc0ocM2oZCk1NbXX/ye2Rc0orUc/tVNe3wJFcxvEXrTvmD3p3OaEpuAsTZ8sldW2QNHSBrEn/e7bGxpZ4p7ZNUt//PEHPvnkE3zyySc4deqUJWMifTD02YigLwxLE3u6G3r30BW2/TF07g6j331LE3u5Gwq7z9Pvvt1RtrahpLoJACVLXDJ5NdzVq1exePFinDhxAn5+fgCA+vp6TJ06FXv37kVERISlYyTQ7WemnyKiN4x1jA3zxdW6FuRVKGibEztTQCNLVhUb5ovy+hbky5RIHEa/+/Ykr2MKLtyPiru5ZPLI0kMPPYS2tjYUFBSgtrYWtbW1KCgogFarxUMPPWSNGAmAS9ca0dKmgZcHH9FBPlyH45RipboklEaW7EuLWoPLHVfW1DbAOmhFnP2iKTj7YPLI0tGjR/Hbb79h9OjRhvtGjx6Nd999FzfeeKNFgyOd9FNwsVJfapZoJVTkbZ8KKxugZYEgHw/agsZKaI84+3XWUH5ByRKXTB5ZioyMRFtbW4/7NRoNwsLCLBIU6YmaUVqf/gujqKoRqnYNx9EQva7bnBDr0I8s6feeJPaDFvbYB5OTpddffx2PP/44/vjjD8N9f/zxB5588kls2bLFosGRTnm0dNTqwsRCiD3d0a5lcbGyketwSAfa5sT6Ivw9IRK6Qa3Rovga/e7bCyruth8mJ0vLli1DTk4OEhMTIRAIIBAIkJiYiOzsbPy///f/EBAQYLgRy9BqWeTRnnBWxzAM9ZyxQ1TcbX3dfvdpGtpudC3uDqDibk6ZXLO0detWK4RB+nOpuglNag2E7jwMD6bO3dY0NswXGZdq6AvDTmi1bOfIErUNsKrYMF9kltQiX6bEwoEPJzbQOQVHv/tcMzlZWrp0qTXiIP3I7VLc7can7fysiYq87UtZXTOa1Bp4uPEwLIguFKyJRpbszzlaCWc36JvXAVCBn+10XRWk1bIcR0P0o0qjQn3oQsHKuv7usyz97tsDQ9uACD9uAyGULDkCWglnO8ODfeDB56FR1Y6rdS1ch+PyDCvhJDQNYW0jQ0Rw5zNQtLShQtHKdTgur6G1DZeouNtuULJk53TF3dS521bc+TyMkuiaftIu7NzLlzUAoHolW/Bw42FEiK7pJ03FcU//uU/F3faBkiU7d7mmCY2q9o4PMurcbQtUu2E/aCWcbdHvvv2g4m77QsmSncvt0pDPnWo2bILaB9gHRUsbyut1U6E0DWcbnXVLNKrKFY2WRUZxDX7OlQHQrdAl3DN5NVxTUxNeffVVpKeno6qqClpt926vly5dslhwpOu+QPSGsZWxHdOdeXR1zSn9qFK4nyfEXu4cR+Ma6EKBW2m5Mqzflw9Zl5qxj05cxqhQEVLipBxGRkxOlh566CEcPXoU9913H6RSKRiG9imzpnNXaemorcVIdHUbMkUrapvUVC/AEZqCsz19slRW2wJFSxvEnpSk2kpargwr9mTjz+sQ65rbsGJPNrYvmUgJE4dMTpZ+/vln/Pjjj7jhhhusEQ/poNGyyCqpwemyOgBADE1D2IxI6I6hgV64UtOMApkSN4wI4jokl9S5zYmI40hch9jLHeF+niivb8F5mRKJwwK5DsklaLQs1u/L75EodbV+Xz5mxUpoI3WOmFwE4+/vT1uZWFlargzTXjuExTsy0dqmm+b8v09OIa1jDptYHxW6ci+fRpY40bXfErGNrJLablNvf8ZCN9KdVVJru6BINyYnSxs3bsQLL7yA5uZma8QzoG3btiEqKgpCoRCJiYnIysrq9/ivvvoKMTExEAqFGDduHH766aduj7MsixdeeAFSqRSenp5ITk7GxYsXrXkK/dIPxf75jVOpbMWKPdmUMNkI1W5wq12jxYWOzYypbYBt0YWC7VU1GNfXytjjiOWZnCy98cYb2L9/P0JDQzFu3DhMnDix282avvjiC6xevRqpqanIzs7GhAkTMGfOHFRVVfV6/G+//YbFixfjwQcfxOnTp7FgwQIsWLAAubm5hmM2b96Md955B++99x4yMzPh7e2NOXPmoLXV9r+U/Q3F6u9bvy8fGuosbXW07Qm3LlU3Qd2uhbcHH5H+XlyH41JoZMn2QkRCix5HLM/kmqUFCxZYIQzjvPnmm1i+fDkeeOABAMB7772HH3/8ETt37sTatWt7HP/2228jJSUFf//73wHoRsUOHjyIf/3rX3jvvffAsiy2bt2K5557DvPnzwcAfPzxxwgNDcV3332HRYsW9RqHSqWCSqUy/FmptMyHiilDsUnDqZbAmvRfGEXXGtHapoHQnc9xRK5FX68UI/UFj2o0bEo/snSxshHqdi083KhlibVNiQ6AVCyEXNHa68UyA0AiFmJKNJXAcMXkZCk1NdUacQxIrVbj1KlTWLduneE+Ho+H5ORkZGRk9PqcjIwMrF69utt9c+bMwXfffQcAKCkpgVwuR3JysuFxsViMxMREZGRk9Jksbdq0CevXrx/kGfVEQ7H2Q+IrRIC3B2qb1LhQ2YDxtDeTTelH9GKpXsnmIvw9IRK6oaG1HcXXGqlmzAb4PAapc2OxYk92j8f0lwqpc2OpuJtDDnPJUF1dDY1Gg9DQ0G73h4aGQi6X9/ocuVze7/H6/5rymgCwbt06KBQKw62srMzk8+kNDcXaD4ZhqHaDQ1TczR363edGSpwU25dMhK+w+xiGRCyktgF2wKiRpYCAAFy4cAFBQUHw9/fvt7dSba3zV+sLBAIIBAKLvy4NxdqX2DBfHC+qptoNDhR07Ak3htoGcCI2zBeZJbXIlymxkOtgXEhKnBQH8irx7elypIyVYOnUKEyJDqARJTtgVLL01ltvQSTSfWht3brVmvH0KSgoCHw+H5WVld3ur6yshEQi6fU5Eomk3+P1/62srIRUKu12THx8vAWjN07XoVgG6JYw0VCs7dHVNTeqGlpR3agCwwCjJZQscYF+97mjvzj726QIqk21I0YlS0uXLu31/23Jw8MDkyZNQnp6uqHIXKvVIj09HStXruz1OUlJSUhPT8eqVasM9x08eBBJSUkAgOjoaEgkEqSnpxuSI6VSiczMTKxYscKap9Mn/VDsn1veS8RCpM6NpaFYG9IXeRfIlNBqWSo0thH9qFJ0kDe8PEwuqyQW0HVFHMuytFODjbS2aXCxStcyI452bbArDvVJtHr1aixduhSTJ0/GlClTsHXrVjQ1NRlWx91///0IDw/Hpk2bAABPPvkkpk+fjjfeeAO333479u7diz/++APvv/8+AN3c/KpVq/DSSy9h5MiRiI6OxvPPP4+wsDBOV/2lxEkxK1aCrJJaVDW0IkQkpKFYDgwL8obAjYcmtQaltc2ICvLmOiSXQNuccG9kiAjufAaKljZUKFoR7ufJdUguoUCmhEbLIshHgFBfy5d6EPM5VLJ0991349q1a3jhhRcgl8sRHx+PtLQ0Q4F2aWkpeLzOmvWpU6fis88+w3PPPYd//OMfGDlyJL777jvExcUZjnnmmWfQ1NSEhx9+GPX19Zg2bRrS0tIgFHJbRM3nMTQEyzE3Pg8xEhHOXFUgX6akZMlGOrc5oWSJKx5uPIwIEaFApkR+hZKSJRvRb5weF+5Lo3l2xqGSJQBYuXJln9NuR44c6XHfnXfeiTvvvLPP12MYBhs2bMCGDRssFSJxIrFhvjhzVYG8CgVuG0dToLZAbQPsQ6zU15AszYoNHfgJZNByy3W/+3FhNAVnbxymdQAhXKBCV9tqbdPgUnUTAJqG41pn3ZKC40hcR26FfmSJkiV7Y3Ky9NFHH3G2LxwhtkZbP9jWxcpGaLQs/L3cqWaDY7Q/om2p2jW4UKlb3BAXThcK9sbkZGnt2rWQSCR48MEH8dtvv1kjJkLsxmiJLxgGqFSqUN2oGvgJZFD0oxhjpFSzwTV9slRW2wJFSxvH0Ti/C/JGtGlY+Hm5U42YHTI5WSovL8fu3btRXV2NGTNmICYmBq+99lq/Ha8JcVQ+AjdEBeoKuwvoCtvq9G0DqF6Je+IuX9rn6Xff6s51FHePCxfThYIdMjlZcnNzwx133IHvv/8eZWVlWL58OT799FMMGTIE8+bNw/fffw+tVmuNWAnhBNUt2Q5tc2JfaBradvT1SmOpuNsuDarAOzQ0FNOmTUNSUhJ4PB7OnTuHpUuXYvjw4b2uTCPEEdEXhm2wLEs9luwMXSjYTl6XkSVif8xKliorK7FlyxaMHTsWM2bMgFKpxA8//ICSkhKUl5fjrrvu4qzTNyGWpk+W8ugLw6qu1rWgobUd7nwGI0J8uA6HgC4UbKVNo0WBnIq77ZnJydLcuXMRGRmJXbt2Yfny5SgvL8fnn3+O5ORkAIC3tzfWrFmDsrIyiwdLCBfGdlxdX7rWiBa1huNonJd+VGlEiAgebtTVxB7oR5YuVjZC3U7lFdai//sVCd0wJMCL63BIL0xuShkSEoKjR48a9lfrTXBwMEpKSgYVGCH2IlgkQJCPB6ob1SisbEB8pB/XITklfXH3GCltnmsvIvw9IRK6oaG1HcXXGml61EoMnbvDqLjbXpl8+TZ9+nRMnDixx/1qtRoff/wxAF1X7KFDhw4+OkLsAMMwhi8Jqt2wHn3bAFoJZz8YhqG6JRvobEZJv/v2yuRk6YEHHoBC0bOja0NDg2FDW0KcDXUztj5qG2CfqG7J+jr3hKPibntlcrLEsmyvw4RXr16FWEz/0MQ50dW1dTW0tqG0VrczAE312Bf63beudo3WkIhSsmS/jK5ZSkhIAMMwYBgGM2fOhJtb51M1Gg1KSkqQkpJilSAJ4Zq+98l5eQM0WhZ8HtUVWNL5jpVAEl8h/L09OI6GdNV1ZKmvi2VivkvVTWht08Lbg4/ojga4xP4YnSwtWLAAAJCTk4M5c+bAx6dzaa+HhweioqKwcOFCiwdIiD2IDvKG0J2HZrUGl2uaMDyYlrZbkn4lnP6LmdiPkSEiuPMZKFraUKFopa04LOzc1c5mlDy6CLNbRidLqampAICoqCjcfffdEAqFVguKEHvD5zGIkfgip6we+RVKSpYsrLMZJa2EszcebjyMCBGhQKZEfoWSkiULM3TupuJuu2ZyzdLSpUspUSIuiQpdrSff0DaAvjDsEdUtWU9eeUe9Em1zYteMGlkKCAjAhQsXEBQUBH9//37nrGtray0WHCH2hL4wrEOjZVEop21O7FlsmC++yabVoJam1bLI6xhZGhdByZI9MypZeuuttyASiQz/TwV+xBXRyJJ1lHQUuHq68xFFBa52yXChQL/7FlVS04QmtQZCdx6GBdHvvj0zKlnqus/bsmXLrBULIXYtRiICwwDXGlSoamhFiIimoy1BX680WiKiVYZ2Sp8sldW2QNHSBrGnO8cROQd9f6VYqS/c+LTFjz0z+V/np59+wv79+3vcf+DAAfz8888WCYoQe+Tl4Wa4+tM3UCSDly+jKTh7J/ZyNxR2n6fRJYuhZpSOw+Rkae3atdBoem4mqtVqsXbtWosERYi9iu0owtTXGZDBo7YBjkG/UvGLP8qQUVwDjZblOCLHl0vF3Q7D5GTp4sWLiI2N7XF/TEwMioqKLBIUIfaKirwtz5AsUdsAu5WWK8PJS7rFO99ml2PxjpOY9tohpOXKOI7McbEs22VPOEqW7J3JyZJYLMalS5d63F9UVARvbypQI86Nirwtq6ZRhUqlCgAwWkIjS/YoLVeGFXuy0ahq73a/XNGKFXuyKWEyU2ltMxpa2+HhxsPIUOrbZu9MTpbmz5+PVatWobi42HBfUVER1qxZg3nz5lk0OELsjX5kqaS6Cc3q9gGOJgPR134NDfSCj8DoHrnERjRaFuv35aO3CTf9fev35dOUnBn0U3BjJCK4U3G33TP5X2jz5s3w9vZGTEwMoqOjER0djTFjxiAwMBBbtmyxRoyE2I1gkQDBIgFYtnM/M2K+zik4GlWyR1kltZApWvt8nAUgU7Qiq4T665nqXLm+czdNwTkCky/lxGIxfvvtNxw8eBBnzpyBp6cnxo8fj5tuuska8RFid8aG+eJI4TXkVygxcYg/1+E4tAJaCWfXqhr6TpTMOY500i8SoeJux2DWuDfDMJg9ezZmz55t6XgIsXux0o5kieqWBo3aBtg3Y3uJUc8x07AsaxhZGkcjSw7BrInSo0ePYu7cuRgxYgRGjBiBefPm4ddff7V0bITYJX2Rdx6tiBsUVbsGRVWNAKhtgL2aEh0AqViIvlqFMgCkYiGmRAfYMiyHV17fgvrmNrjxGIySUHG3IzA5WdqzZw+Sk5Ph5eWFJ554Ak888QQ8PT0xc+ZMfPbZZ9aIkRC7oq+vOS9Tol2j5Tgax1VU1Yh2LQtfoRvCxDQyYY/4PAapc3WtYv6cMOn/nDo3ljqvm0hf3D0qVASBG5/jaIgxTE6WXn75ZWzevBlffPGFIVn64osv8Oqrr2Ljxo3WiJEQuzI00BteHnyo2rW4XNPEdTgOS9+raozUl/abtGMpcVJsXzIRkj8ltAHeHti+ZCJS4qQcRea4cmkKzuGYnCxdunQJc+fO7XH/vHnzUFJSYpGgCLFnfB6DGImugSJNxZlP3zaA6pXsX0qcFMefvQWfL78e8ZF+AIBlN0RRomSmzmaU9LvvKExOliIjI5Gent7j/l9++QWRkZEWCao3tbW1uPfee+Hr6ws/Pz88+OCDaGxs7Pf4xx9/HKNHj4anpyeGDBmCJ554AgpF920qGIbpcdu7d6/VzoM4B2pOOXi0zYlj4fMYJA0PxNwJYQCAM2W05Y85WJalPeEckMmr4dasWYMnnngCOTk5mDp1KgDgxIkT2LVrF95++22LB6h37733QiaT4eDBg2hra8MDDzyAhx9+uM86qYqKClRUVGDLli2IjY3FlStX8Mgjj6CiogJff/11t2M/+ugjpKSkGP7s5+dntfMgzmFsx3Jf2vbEPCzLokBOPZYcUcIQPwBATlkdWJalKVQTVSpVqG5Ug89jaFTVgZicLK1YsQISiQRvvPEGvvzySwDAmDFj8MUXX2D+/PkWDxAACgoKkJaWht9//x2TJ08GALz77ru47bbbsGXLFoSFhfV4TlxcHL755hvDn4cPH46XX34ZS5YsQXt7O9zcOk/dz88PEonE6HhUKhVUKpXhz0olfWG6mq57xNEXhulkilbUN7eBz2MwIoRWAzmSsWG+8ODzUN2oxtW6FkQGeHEdkkPRjyqNCPaB0J2Kux2FWa0D7rjjDhw/fhw1NTWoqanB8ePHrZYoAUBGRgb8/PwMiRIAJCcng8fjITMz0+jXUSgU8PX17ZYoAcBjjz2GoKAgTJkyBTt37gTL9t+6f9OmTRCLxYabNacfiX0aLRGBxwA1TWpUNagGfgLpRj8FR18YjkfgxjdMnWaX1nEcjeM5R1NwDskhNqSRy+UICQnpdp+bmxsCAgIgl8uNeo3q6mps3LgRDz/8cLf7N2zYgC+//BIHDx7EwoUL8eijj+Ldd9/t97XWrVsHhUJhuJWVlZl2QsThCd35GB6sGxGhqTjTdXbuFnEcCTGHfirudGk9p3E4ojwq7nZIRk3D+fv7Gz3NUFtr/B5Ba9euxWuvvdbvMQUFBUa/Xl+USiVuv/12xMbG4sUXX+z22PPPP2/4/4SEBDQ1NeH111/HE0880efrCQQCCASCQcdFHFtsmC8uVjUiX6bEzTEhAz+BGFDnbseWMMQfH524jNM0smQy6tztmIxKlrZu3WqVH75mzRosW7as32OGDRsGiUSCqqqqbve3t7ejtrZ2wFqjhoYGpKSkQCQS4b///S/c3d37PT4xMREbN26ESqWihIj0K1bqi+9zKmhkyQzUNsCxJXS0D8irUKK1TUNTqUaqamhFpVIFhqHffUdjVLK0dOlSq/zw4OBgBAcHD3hcUlIS6uvrcerUKUyaNAkAcOjQIWi1WiQmJvb5PKVSiTlz5kAgEOB///sfhMKBuwTn5OTA39+fEiUyIGofYJ5mdbuhmSd9YTimCH9PBPkIUN2oQl6FApOG0nYnxtD3ZRsW5A1vgVlbsxKOmFWzVFxcjOeeew6LFy82jPj8/PPPyMvLs2hwemPGjEFKSgqWL1+OrKwsnDhxAitXrsSiRYsMK+HKy8sRExODrKwsALpEafbs2WhqasKHH34IpVIJuVwOuVwOjUYDANi3bx8++OAD5ObmoqioCNu3b8crr7yCxx9/3CrnQZyLfkXc5ZomNKraOY7GcZyXN4BlgWCRAMEiuihxRAzDUN2SGXKv0hScozI5WTp69CjGjRuHzMxMfPvtt4bGkGfOnEFqaqrFA9T79NNPERMTg5kzZ+K2227DtGnT8P777xseb2trQ2FhIZqbmwEA2dnZyMzMxLlz5zBixAhIpVLDTV+Q7e7ujm3btiEpKQnx8fH4z3/+gzfffNOq50GcR6CPABJfIVgWKJTT6JKxum5zQhwXJUum6+zcTcmSozF5HHDt2rV46aWXsHr1aohEnStZbrnlFvzrX/+yaHBdBQQE9LtRb1RUVLcl/zNmzBiwBUBKSkq3ZpSEmCo2zBdyZSvyKpQ0FWEkQ+duSpYc2sQh/gBARd4m0G+gq29qSxyHySNL586dwx133NHj/pCQEFRXV1skKEIcRdfmlMQ41DbAOYyPEIPHABWKVsgVrVyHY/dqm9Qor28BAIyltgEOx+Rkyc/PDzKZrMf9p0+fRnh4uEWCIsRRUJG3abRaFuflupVwNLLk2Lw83BAj0f0b5pTR6NJA9P2VogK94Cvsf1U2sT8mJ0uLFi3Cs88+C7lcDoZhoNVqceLECTz99NO4//77rREjIXZL/4V/Xt6Ado2W42js35XaZjSrNfBw4yE6yJvrcMggUd2S8ahzt2MzOVl65ZVXEBMTg8jISDQ2NiI2NhY33XQTpk6diueee84aMRJit4YEeMFH4AZ1uxaXqpu4Dsfu6afgYiQiuPEdYgMB0o+Ejrol2vZkYHkd9UqULDkmkwu8PTw8sGPHDjz//PPIzc1FY2MjEhISMHLkSGvER4hd4/EYjJGK8PvlOuRXKDEqlOpw+mOoV5LQFJwz0I8snb2qQJtGC3dKgPtkGFmi4m6HZPJv9vHjxwEAQ4YMwW233Ya77rqLEiXi0gxF3lS31C+NlsWJi7pFIEJ3HjTa/lerEvsXHegNsac7VO1anO/oyk56UjS3obRW19aG9oRzTCYnS7fccguio6Pxj3/8A/n5+daIiRCHoi/y1hdwkp7ScmWY9tohZJfVAwB2Z1zBtNcOIS2352IR4jh4PAbxHVufnKYi7z7lyXSfDRH+nvDz8uA4GmIOk5OliooKrFmzBkePHkVcXBzi4+Px+uuv4+rVq9aIjxC7FyvVDavnVygH7O3litJyZVixJxuyPy0vlytasWJPNiVMDo6KvAeWS5vnOjyTk6WgoCCsXLkSJ06cQHFxMe68807s3r0bUVFRuOWWW6wRIyF2bWSoD/g8BnXNbZArqd9MVxoti/X78tFbCqm/b/2+fJqSc2AJ1JxyQLlU3O3wBlWNFx0djbVr1+LVV1/FuHHjcPToUUvFRYjDELrzMSLYBwA1p/yzrJLaHiNKXbEAZIpWZJXU2i4oYlH6abjLNc2obVJzG4yd0m9zMjaM6pUcldnJ0okTJ/Doo49CKpXinnvuQVxcHH788UdLxkaIw9B/CFKy1F1Vg3EjbcYeR+yP2NMdI0J0FwvUnLKnRlU7SjraitDIkuMyOVlat24doqOjccstt6C0tBRvv/025HI5PvnkE9pnjbgs6uTduxCR0KLHEfuUoC/yprqlHnS1jIBULESQj4DrcIiZTO6zdOzYMfz973/HXXfdhaCgIGvERIjDofYBvZsSHQCpWNjnVBwDQCIWYko0bULsyBKG+OOrU1cpWeqFvr8SbZ7r2ExOlk6cOGGNOAhxaGM6kqUrNc1QtrbR3k8d+DwGqXNj8cie7B6PMR3/TZ0bCz6P6fE4cRz6FXE5ZfXQaFn69+wij1bCOQVqt0qIBfh7eyBMrJtKouZ83aXESQ3TNF1JxEJsXzIRKXFS2wdFLGpUqAheHnw0qtpRVNXIdTh2RV/cTc0oHZvJI0uEkN7FhvmiQtGK/AoFTSt1wbIsrnR0L35xbiz8vT0QItJNvdEIhHPg8xhMiPBDxqUanC6tw2gJbfsDAM3qzuSRRpYcG40sEWIhVLfUu4tVjahtUkPozsM9iUMxPz4cScMDKVFyMtScsqcCWQO0LBAsEiDElxYxODKTkiWNRoNjx46hvr7eSuEQ4rhiOwo4KVnqLvNSDQBg0lB/eLjR9ZmzMjSnpPYBBrmGzXNpCs7RmfTJxefzMXv2bNTV0ZuBkD/T91q6IG9Em0bLcTT24+QlXcPJxOhAjiMh1qQfWbpY1Qhlaxu3wdgJ2ubEeZh8mRcXF4dLly5ZIxZCHFqEvydEAjeoNVoUX6MiV0BXr5RZohtZun4YJUvOLMhHgCEBXmBZ4GwZbSoNALkdTWrHUrLk8ExOll566SU8/fTT+OGHHyCTyaBUKrvdCHFVDMNgTMfoUl45vRcAoPhaI6ob1RC48TAhkr4wnF1n3RLNPrS2aXCxUrcylkaWHJ/Jq+Fuu+02AMC8efPAMJ0FmizLgmEYaDQay0VHiIOJlfoiq6QW+TIlFnIdjB3QT8FNHOIPgRuf42iItSVE+uH7nAqcLqvnOhTOFcob0K5lEeDtAamYirsdncnJ0uHDh60RByFOIZb2iOvmZEdxd+IwaqXgCgxF3qV1hgtoV9XZudvXpf8enIXJydL06dOtEQchTmFslz3iXP3LQlevpBtZonol1zBG6gsPNx7qmttwuaYZ0UHeXIfEmbwKKu52JkYlS2fPnkVcXBx4PB7Onj3b77Hjx4+3SGCEOKKRISK48xkoWtpQoWhFuJ8n1yFx5lJ1E641qODhxkN8Lx28ifPxcONhXLgYp67U4XRpnUsnS7kddYtxlCw5BaOSpfj4eMjlcoSEhCA+Ph4Mw4Bl2R7HUc0ScXUebjyMCBGhQKZEfoXSpZOlzI56pYRIPwjdqV7JVSRE+nUkS/X468QIrsPhhLpdi0K5rrg7jjbQdQpGJUslJSUIDg42/D8hpG+xUl9DsjQrNpTrcDijr1eiKTjXoqtbKnHp5pQXKhug1mjhK3RDZIDrXjA5E6OSpaFDh/b6/4SQnmLDfPFNdmfNgitiWZaKu12Uvn1AgawBLWoNPD1cb1TR0Lk7XOzSdYvOxOyNdPPz81FaWgq1Wt3t/nnz5g06KEIcGe0RB1yuaUZVgwoefB4mdqyQIq5BKhYi1FeASqUK58pdc1PpXCrudjomJ0uXLl3CHXfcgXPnznWrXdJnz1SzRFydPlm6WtcCRUsbxJ7uHEdke/pRpXiqV3I5DMMgIdIfaXlynC6tc81kqZw6dzsbkzt4P/nkk4iOjkZVVRW8vLyQl5eHY8eOYfLkyThy5IgVQiTEsYi93BHhr6tTKHDR0aVMQ72S631REmDiUD8AwOnSek7j4EK7Rmt439MGus7D5GQpIyMDGzZsQFBQEHg8Hng8HqZNm4ZNmzbhiSeesEaMAIDa2lrce++98PX1hZ+fHx588EE0Nva//9aMGTPAMEy32yOPPNLtmNLSUtx+++3w8vJCSEgI/v73v6O9vd1q50Fcg2EqzgWbU+rqlTo2z6Xibpekb06Z3dGc0pUUXWuEql0LH4EbogJdt3WCszE5WdJoNBCJRACAoKAgVFRUANAVfhcWFlo2ui7uvfde5OXl4eDBg/jhhx9w7NgxPPzwwwM+b/ny5ZDJZIbb5s2bDY9pNBrcfvvtUKvV+O2337B7927s2rULL7zwgtXOg7iG2DDXrVsqrW2GXNkKdz5D9UouKi5MDDceg6oGFSoUrVyHY1PnrurqlWLDfMHjUXG3szC5ZikuLg5nzpxBdHQ0EhMTsXnzZnh4eOD999/HsGHDrBEjCgoKkJaWht9//x2TJ08GALz77ru47bbbsGXLFoSFhfX5XC8vL0gkkl4fO3DgAPLz8/HLL78gNDQU8fHx2LhxI5599lm8+OKL8PDwsMr5EOfnyiNLXeuVXHElFAE8PfgYI/XFuXIFTpfWuVS/sbyO9zwVdzsXk0eWnnvuOWi1WgDAhg0bUFJSghtvvBE//fQT3nnnHYsHCOim/vz8/AyJEgAkJyeDx+MhMzOz3+d++umnCAoKQlxcHNatW4fm5uZurztu3DiEhnb2wpkzZw6USiXy8vL6fE2VSgWlUtntRkhX+pGli1UNULdrOY7GtvTNKBOjaQrOlelbCLha3VJn2wCqV3ImJo8szZkzx/D/I0aMwPnz51FbWwt/f3+r9ZPQdw/vys3NDQEBAZDL5X0+75577sHQoUMRFhaGs2fP4tlnn0VhYSG+/fZbw+t2TZQAGP7c3+tu2rQJ69evN/d0iAsI9/OESMBHg0qDHb9ewsQh/pgSHQC+kw/Ld+2vRM0oXVvCED98nHEFp0tdpzmlRssaRpaoc7dzMbvPUlcBAeateFm7di1ee+21fo8pKCgw67UBdKtpGjduHKRSKWbOnIni4mIMHz7c7Nddt24dVq9ebfizUqlEZGSk2a9HnM/+PDlUHSNKr+/X1fJJxUKkzo1FSpyUy9Cs6mpdCyoUrXDjMYYVUcQ1JUTq6tVyK5RQtWsgcHP+KdmS6ka0tGng6c7HsGAfrsMhFmSRZMlca9aswbJly/o9ZtiwYZBIJKiqqup2f3t7O2pra/usR+pNYmIiAKCoqAjDhw+HRCJBVlZWt2MqKysBoN/XFQgEEAgERv9c4lrScmVYsScbf14DJFe0YsWebGxfMtFpE6aMjlGlCZF+8PLg9OOFcGxooBf8vdxR19yGAlmDS2ymrO+vFBvm6/SjyK6G00+z4OBgw55z/UlKSkJ9fT1OnTqFSZMmAQAOHToErVZrSICMkZOTAwCQSqWG13355ZdRVVVlmOY7ePAgfH19ERsba+LZEKIbhl+/L79HogQALAAGwPp9+ZgVK3HKD1PDFicu2IiQdMcwDBKG+OPQ+SqcLq1ziWTpnL5eiforOR2TC7y5MGbMGKSkpGD58uXIysrCiRMnsHLlSixatMiwEq68vBwxMTGGkaLi4mJs3LgRp06dwuXLl/G///0P999/P2666SaMHz8eADB79mzExsbivvvuw5kzZ7B//34899xzeOyxx2jkiJglq6QWsn6WSrMAZIpWZJXU2i4oG9IXd1O9EgGAhI4EyVWKvLvuCUeci0MkS4BuVVtMTAxmzpyJ2267DdOmTcP7779veLytrQ2FhYWG1W4eHh745ZdfMHv2bMTExGDNmjVYuHAh9u3bZ3gOn8/HDz/8AD6fj6SkJCxZsgT3338/NmzYYPPzI86hqsG4njLGHudIymqbUV7fAj6PwaSh1F+JABM7fg9Olzl/kbe2a3E3JUtOx2GKCgICAvDZZ5/1+XhUVFS3TrGRkZE4evTogK87dOhQ/PTTTxaJkZAQkdCixzmSzI7RsvERYngLHOajhVjR+AgxGAYoq23BtQYVgkXOO2J/pbYZjap2CNx4GBlCxd3OxmFGlghxBFOiAyAVC9FXNRID3ao4Z9xctLNeiabgiI5I6I5RIbodH5y9hYB+Ci5G6gs3Pn21Ohv6FyXEgvg8BqlzdYsD+kqYUufGOmVxd2YJbZ5LejI0pyyr5zQOa8ul4m6nRskSIRaWEifF9iUTIRF3n2oTCdyctm1AeX0Lymp19UqToyhZIp06O3k7+chShS5Zom1OnBMVFhBiBSlxUsyKlSCrpBbf55Rj7+9lGBro5ZSJEgBkdkzBxYWL4UP1SqSLhI7NlM9eVaBdo3XKKSqWZQ09lqi42zk5328tIXaCz2OQNDwQa2aPBsPoOhnLFC1ch2UVnVuc0KgS6W5EsA9EAjc0qzW4UNnIdThWcbWuBYqWNrjzGYwKFXEdDrECSpYIsbJgkQATO66uf8mv5Dga69CvhLueirvJn/B4DCbo+y05aQsBfb3SaIkIHm70teqM6F+VEBuYFavboPmAEyZLFfUtuFLTDB4DTI6i/kqkp866pXpO47CWzs7dNAXnrChZIsQG9MnSyUs1ULa2cRyNZelXwcWFiyESunMcDbFHzl7knUvNKJ0eJUuE2MDwYB8MC/ZGm4bF0cJrXIdjUbTFCRlIQqRuxLH4WhMUzc51saAr7qZtTpwdJUuE2Ih+dOmgk03F0ea5ZCD+3h6IDvIGAORcrec2GAuTKVpR26QGn8cgRkLF3c6KkiVCbGR2R7J0uLAKbRotx9FYhlzRisuGeiVKlkjf9JvqZl9xrqk4/ajSyBAfCN35HEdDrIWSJUJsJD7SH0E+HmhobTdMXTk6fb3S2DAxxJ5Ur0T65qydvGkKzjVQskSIjfB5DGbG6Kfi5BxHYxknO5I+moIjA9E3p8wprYNWyw5wtOPQF3dT527nRskSITbUtW6JZR3/CyPT0IySirtJ/0ZLRBC686Bsbcel6iauw7EYQ9uAcNoTzplRskSIDU0bGQRPdz4qFK3I67gidVRVylZcqm4CwwDX0cgSGYA7n4fx4X4AnKeFQJWyFdcaVOAxwBgpJUvOjJIlQmxI6M7HjSODADj+qriTHV27Y6W+VK9EjOJMdUsaLYuvT5UBAKRiIQRuVNztzChZIsTGnKWFQKahZQBNwRHjOEsn77RcGaa9dgib918AAJTXt2Laa4eQlivjODJiLZQsEWJjM8eEgscA+TIlrtY1cx2O2WjzXGIqfZF3oVyJJlU7x9GYJy1XhhV7siFTtHa7X65oxYo92ZQwOSlKlgixsQBvD0weqkswHHVj3aqGVhRf09UrTaF6JWKkUF8hwv08oWWBs1cVXIdjMo2Wxfp9+ehtaYb+vvX78qFxotV+RIeSJUI4MHusY2+sm9VRrxQj8YWflwfH0RBHEm+oW3K8Iu+sktoeI0pdsdB19Na/P4jzoGSJEA7o65YyS2odcq8smoIj5urs5F3PaRzmqGroO1Ey5zjiOChZIoQDQwO9MSrUBxoti8OFVVyHY7JMQzNKKu4mpjE0pyyrc7heYyEioUWPI46DkiVCOOKoq+KqG1W4WNUIgDp3E9ONDfOFO59BdaMaV+tauA7HJFOiAyAV950IMdC1EaA6PudDyRIhHJkVKwEAHCmsgqpdw3E0xuusVxLB35vqlYhphO58xIbptgbJdrDmlHweg9S5sb0+xnT8N3VuLPg8ptdjiOOiZIkQjowPFyNEJECTWoOM4hquwzHaSdrihAySvm7JEfstxUf6o7dUSCIWYvuSiUiJk9o8JmJ9blwHQIir4vEYJMeG4rPMUhzMr8SM0SFch2SUTNo8lwxSwhA/7PrNMTt5f/F7GVgA10X5Y/Ws0ahqaEWISDf1RiNKzotGlgjhkL5u6ZeCSofYib22SY3CygYA1F+JmG9iR5F3foUCrW2OMwWt0bL44vdSAMCS64ciaXgg5seHI2l4ICVKTo6SJUI4NHV4ILw9+KhUqgy7l9uzrBLdFNyoUB8E+gg4joY4qgh/TwT5eKBNwzrUhtJHCqtQoWiFv5c75oyVcB0OsSFKlgjhkMCNj+mjgwE4xqq4kx1TcFSvRAaDYRhDC4HTDlTk/VmmblRp4cQICN1p41xXQskSIRxzpBYCJ2nzXGIhhk11HaRuqaK+xdATbXHiEI6jIbZGyRIhHLt5dAj4PAaFlQ0orbHfjXXrmtQ4L9fVKyVS524ySAmRHSNLVxxjZOmL38ugZXVd64cH+3AdDrExh0mWamtrce+998LX1xd+fn548MEH0djY2Ofxly9fBsMwvd6++uorw3G9Pb53715bnBIhAAA/Lw9MidIlHwfy5RxH07fMjv5KI0N8EET1SmSQxkeIwWOACkUr5P3st2YP2jVafPF7GQDgnsShHEdDuOAwydK9996LvLw8HDx4ED/88AOOHTuGhx9+uM/jIyMjIZPJut3Wr18PHx8f3Hrrrd2O/eijj7odt2DBAiufDSHdOcJUXGZHcTeNKhFL8Ba4YbTEF4Bu6xN7drjwGuTKVgR4e2BOxybYxLU4RLJUUFCAtLQ0fPDBB0hMTMS0adPw7rvvYu/evaioqOj1OXw+HxKJpNvtv//9L+666y74+HQfQvXz8+t2nFBI+/oQ29InS79frkVdk5rjaHpHxd3E0gx1S3benPKzzCsAgL9NioDAjQq7XZFDJEsZGRnw8/PD5MmTDfclJyeDx+MhMzPTqNc4deoUcnJy8OCDD/Z47LHHHkNQUBCmTJmCnTt3Dri5o0qlglKp7HYjZDAiA7wQIxFBywKHztvfxrr1zWqcl+t+z6m/ErEUR+jkfbWuGUcuXAMALJ5Chd2uyiGSJblcjpCQ7t2N3dzcEBAQALncuBqPDz/8EGPGjMHUqVO73b9hwwZ8+eWXOHjwIBYuXIhHH30U7777br+vtWnTJojFYsMtMjLStBMipBezO/q22GPdUlZJLVgWGB7sTTuqE4vRtw84W16PNo2W42h69+XvZWBZXU+06CBvrsMhHOE0WVq7dm2fRdj62/nz5wf9c1paWvDZZ5/1Oqr0/PPP44YbbkBCQgKeffZZPPPMM3j99df7fb1169ZBoVAYbmVlZYOOkZDZHVNxxy5U211XY31xdyJNwRELGhbkDV+hG1rbtCjsWGlpT9o1Wnzxh76wm0aVXBmne8OtWbMGy5Yt6/eYYcOGQSKRoKqq+9REe3s7amtrIZEM3EX166+/RnNzM+6///4Bj01MTMTGjRuhUqkgEPS+4kcgEPT5GCHmGhvmizCxEBWKVpwoqsbMMfZTSEqb5xJr4PEYxA/xx7EL13C6tA5x4WKuQ+om/XwVKpUqBHp7YHYsdex2ZZwmS8HBwQgODh7wuKSkJNTX1+PUqVOYNGkSAODQoUPQarVITEwc8Pkffvgh5s2bZ9TPysnJgb+/PyVDxOYYRrex7scZV3Awv9JukiVFSxvyZbp6peupXolY2MQhfh3JUj3uS+I6mu70Hbv/NjkCHm4OUbVCrMQh/vXHjBmDlJQULF++HFlZWThx4gRWrlyJRYsWISwsDABQXl6OmJgYZGVldXtuUVERjh07hoceeqjH6+7btw8ffPABcnNzUVRUhO3bt+OVV17B448/bpPzIuTPOjfWrbKbjXV/76hXGhbkjRBfqlcilqWvWzpRXI3vc8qRUVwDjR387pfVNuPYxY7C7utoCs7VcTqyZIpPP/0UK1euxMyZM8Hj8bBw4UK88847hsfb2tpQWFiI5ubuHZB37tyJiIgIzJ49u8druru7Y9u2bXjqqafAsixGjBiBN998E8uXL7f6+RDSm8ToQIgEbqhuVOF0WT0mDfXnOqQu/ZVoCo5YXnWDCgBQqVThyb05AACpWIjUubFIiZNyFtfe30vBssC0EUGIosJul8ewA62TJwNSKpUQi8VQKBTw9fXlOhzi4B7//DT2nanAI9OHY+2tMVyHg7nvHse5cgXeXhSP+fHhXIdDnEhargwr9mTjz19CTMd/ty+ZyEnC1KbRYuqrh3CtQYV/3zsRt43jLmkj1mXs97dDTMMR4ko6u3lz30JA2dqGvAoFANo8l1iWRsti/b78HokSAMN96/flczIll15QiWsNKgT5CAzvR+LaKFkixM7MGB0Mdz6D4mtNuHSt7/0PbeGPy7XQskBUoBckYqpXIpaTVVILWT97wrEAZIpWZHW0rbClTzsKu++aHAF3Pn1NEkqWCLE7vkJ3wxJ9rveKoy1OiLVUNRi3ea6xx1lKaU0zfr1YDQBYRIXdpAMlS4TYIXvZWDfzEm2eS6zD2E7wtu4Y//nvulGlG0cGYUigl01/NrFflCwRYoeSO3osnSqtQ3WjipMYGlrbcK6c6pWIdUyJDoBULDQUc/8ZA92qOFvuRahu1+Krjo7d91LHbtIFJUuE2KEwP0/EhfuCZYFDBdxsrPvHlTpoWWBIgBfC/Dw5iYE4Lz6PQercWADoNWFiAaTOjQWf11c6ZXm/FFSiulGNYJHAbprCEvtAyRIhdmrWGP3GutxMxXVucUJTcMQ6UuKk2L5kYq+LB9x4DIYF+9g0Hn3H7rsnR1JhN+mGfhsIsVP6uqXjRdfQorb9xrqZVNxNbCAlTorjz96Cz5dfj7cXxePz5Ym4aWQQ2rUsnvj8NFTttvndv1zdhONF1WAY4O7rIm3yM4njoGSJEDs1RipChL8nWtu0+LVj2wVbaVS1d9YrUbJErIzPY5A0PBDz48ORNDwIW+6agEBvD5yXN+D1tEKbxKAv7L5pZDAiA6iwm3RHyRIhdophGM5WxZ26UgeNlkVkgCfCqV6J2FiISIjNfxsPAPjgeAmOXbDuxYK6XYuv/7gKALiHCrtJLyhZIsSO6ZOl9PNVNu1krK9XolVwhCszx4TivuuHAgDWfHUGtU1qq/2sA/ly1DSpEeorwMyYEKv9HOK4KFkixI5NiQqA2NMdtU1qnLpSZ7Of21ncTckS4c4/bhuDESE+uNagwrPfnIW1tjLtWtjtRoXdpBf0W0GIHXPj83BLx5WuLfaK02hZHD5fhTNl9QCAyUP9rf4zCemLpwcfby+Khwefh4P5lfgsq9TiP6Okugm/FdfoCrun0BQc6R0lS4TYua51S9a6sgZ0O8BPe+0QHtj1O/Qzfot3nERarsxqP5OQgYwNE+OZlNEAgI0/5KOoyrL7JX7ekYDNGBVM9XmkT5QsEWLnbhoVDA8+D5drmi3+RaGXlivDij3ZPTY2lStasWJPNiVMhFP/74ZoTBsRhNY2LZ7ca7l2Aqp2Db4+pS/sHmqR1yTOiZIlQuycj8ANU0foaoes0aBSo2Wxfl8+ehuz0t+3fl++TQvMCemKx2Pwxl0T4O/ljrwKJd48cMEir7s/rxK1TWpIfIW4eXSwRV6TOCdKlghxANZsIZBVUttjRKkrFoBM0YqsklqL/2xCjBXqK8SrC3XtBP5z7BJOFFUP+jU/y7wCQNeEkgq7SX/ot4MQB6DfWDenrB5Vyr4TG3OclymNOq6qwbI/lxBTzRkrweKOIuzVX+agbhDtBIqvNeLkpVrwGGDRFOrYTfpHyRIhDiDUV4gJkX4AgF8stLFubZMaG/bl4+WfCow6PkTUc/8uQmzt+b+MwbBgb1QqVVj7rfntBD7vaBdwS0wIpGIq7Cb9o2SJEAcx2zAVN7gWAk2qdryTfhE3bT6MnSdK0K5l4dHPFAQDQCoWYko0bahLuOfl4YZ3FiXAnc9gf14lvvyjzOTXaG3T4OtsXWH3YmoXQIxAyRIhDkJft3SiuAZNqnaTn69u12L3b5cx/fXDePPgBTSq2hEX7otPHpyCdxbHg4EuMepK/+fUubHg8/78KCHciAsXY81sXTuBF/+Xj0vXTFslmpYrR31zG8LEQswYTR27ycAoWSLEQYwM8cHQQC+o27Um7ZWl1bL4PqccyW8eRer/8lDdqMbQQC+8uzgB/3tsGm4cGYyUOCm2L5kIibj7VJtELMT2JROREie19OkQMigP3zgMScMC0dKmwZN7c6Bu1xr9XH1zy7uvG0IXAcQoblwHQAgxDsMwmDUmFB8cL8HB/ErcOq7/BIZlWRy7WI3NaeeRV6Er4g7yEeDJ5JFYdF0k3P809ZYSJ8WsWAmySmpR1dCKEJFu6o2+TIg94vEYvHn3BKRs/RXnyhXY+ssFPJMSM+DziqoakFVSCz6Pwd3XUWE3MQ4lS4Q4kFmxumTpUGEV2jXaPpc755TV47WfzyOjY483kcAN/zd9GP7ftGh4efT9tufzGCQNp/3giGOQij3x6l/HYcWn2dh+tBg3jgwe8Pf3s0xdjdMtMSE9RlIJ6QtNwxHiQCYN9Ye/lzvqm9uw9ZeLyCiu6dYssvhaI1bsOYUF204g41INPPg8PDQtGkefuRkrbxnZb6JEiCO6dZwUd02OAMvq2gkomtv6PLa1TYNvsvUdu6mwmxiPPjkJcSC/FFSitU1Xm/Gvw0X41+EiSMVCPDFzJM5erceXf1yFRsuCYYC/JkTgqVkjEeHvxXHUhFhX6tyxyCqpxeWaZvzjv+fwr3sSwDA9p49/OieDoqUN4X6euGkkdewmxqNkiRAHod+/7c9dZWSKVqz79pzhz8ljQvD3OTEYLRHZNkBCOOItcMPbixKwcPtv+PGcDDNOBePOyT3rkfSb5i66LpJq8YhJaBqOEAfQ3/5teu58Bnsfvh4fLL2OEiXiciZE+uGpWaMAAC/+Lw+Xq5u6PX6hsgG/X64Dn8fgLirsJiaiZIkQBzDQ/m0A0KZhYWYzY0KcwiPTh2NKdACa1Bqs+iIHbZrOdgKfdXTsTh4TglBfKuwmpqFkiRAHYOy+bLR/G3FlfB6Dt+6Oh0johpyyemz95QIyimvw9akyfPm7Llm6J3Eox1ESR0Q1S4Q4AGP3ZaP924irC/fzxCt3jMPjn5/GtsPF2Ha42PAYnwGaWk3vfk+Iw4wsvfzyy5g6dSq8vLzg5+dn1HNYlsULL7wAqVQKT09PJCcn4+LFi92Oqa2txb333gtfX1/4+fnhwQcfRGOjaa3zCbG2KdEBkIqFPbYj0aP92wjp5M7v/Z2iYYHHPstGWq7MxhERR+cwyZJarcadd96JFStWGP2czZs345133sF7772HzMxMeHt7Y86cOWht7ZyquPfee5GXl4eDBw/ihx9+wLFjx/Dwww9b4xQIMRufxyB1biwA2r+NkP7oF0P0Z/2+/G79yQgZCMOyjlUSumvXLqxatQr19fX9HseyLMLCwrBmzRo8/fTTAACFQoHQ0FDs2rULixYtQkFBAWJjY/H7779j8uTJAIC0tDTcdtttuHr1KsLCwoyKSalUQiwWQ6FQwNfXd1DnR0h/0nJlWL8vv1uxt1QsROrcWNq/jRAAGcU1WLzj5IDHfb78eupWT4z+/nbamqWSkhLI5XIkJycb7hOLxUhMTERGRgYWLVqEjIwM+Pn5GRIlAEhOTgaPx0NmZibuuOOOXl9bpVJBpVIZ/qxUKq13IoR0Qfu3EdI/WgxBrMFpkyW5XA4ACA0N7XZ/aGio4TG5XI6QkJBuj7u5uSEgIMBwTG82bdqE9evXWzhiQoxD+7cR0jdaDEGsgdOapbVr14JhmH5v58+f5zLEXq1btw4KhcJwKysr4zokQgghoMUQxDo4HVlas2YNli1b1u8xw4YNM+u1JRIJAKCyshJSaWctR2VlJeLj4w3HVFVVdXtee3s7amtrDc/vjUAggEAgMCsuQggh1qNfDLFiTzYYoFvXe1oMQczFabIUHByM4GDrbGYYHR0NiUSC9PR0Q3KkVCqRmZlpWFGXlJSE+vp6nDp1CpMmTQIAHDp0CFqtFomJiVaJixBCiHWlxEmxfcnEHoshJLQYgpjJYWqWSktLUVtbi9LSUmg0GuTk5AAARowYAR8fHwBATEwMNm3ahDvuuAMMw2DVqlV46aWXMHLkSERHR+P5559HWFgYFixYAAAYM2YMUlJSsHz5crz33ntoa2vDypUrsWjRIqNXwhFCCLE/tBiCWJLDJEsvvPACdu/ebfhzQkICAODw4cOYMWMGAKCwsBAKhcJwzDPPPIOmpiY8/PDDqK+vx7Rp05CWlgahsLOw79NPP8XKlSsxc+ZM8Hg8LFy4EO+8845tTooQQojV0GIIYikO12fJHlGfJUIIIcTxGPv97TAdvAkhhBBCuEDJEiGEEEJIPyhZIoQQQgjpByVLhBBCCCH9oGSJEEIIIaQflCwRQgghhPSDkiVCCCGEkH5QskQIIYQQ0g+H6eBtz/R9PZVKJceREEIIIcRY+u/tgfpzU7JkAQ0NDQCAyMhIjiMhhBBCiKkaGhogFov7fJy2O7EArVaLiooKiEQiMIzlNmlUKpWIjIxEWVmZU2+j4grnSefoPFzhPF3hHAHXOE86x/6xLIuGhgaEhYWBx+u7MolGliyAx+MhIiLCaq/v6+vrtL/kXbnCedI5Og9XOE9XOEfANc6TzrFv/Y0o6VGBNyGEEEJIPyhZIoQQQgjpByVLdkwgECA1NRUCgYDrUKzKFc6TztF5uMJ5usI5Aq5xnnSOlkEF3oQQQggh/aCRJUIIIYSQflCyRAghhBDSD0qWCCGEEEL6QckSIYQQQkg/KFni2LZt2xAVFQWhUIjExERkZWX1e/xXX32FmJgYCIVCjBs3Dj/99JONIjWfKeeYl5eHhQsXIioqCgzDYOvWrbYLdJBMOc8dO3bgxhtvhL+/P/z9/ZGcnDzgv709MOUcv/32W0yePBl+fn7w9vZGfHw8PvnkExtGaz5T35d6e/fuBcMwWLBggXUDtABTznHXrl1gGKbbTSgU2jBa85n6b1lfX4/HHnsMUqkUAoEAo0aNsvvPWVPOccaMGT3+LRmGwe23327DiE1n6r/j1q1bMXr0aHh6eiIyMhJPPfUUWltbzQ+AJZzZu3cv6+Hhwe7cuZPNy8tjly9fzvr5+bGVlZW9Hn/ixAmWz+ezmzdvZvPz89nnnnuOdXd3Z8+dO2fjyI1n6jlmZWWxTz/9NPv555+zEomEfeutt2wbsJlMPc977rmH3bZtG3v69Gm2oKCAXbZsGSsWi9mrV6/aOHLjmXqOhw8fZr/99ls2Pz+fLSoqYrdu3cry+Xw2LS3NxpGbxtTz1CspKWHDw8PZG2+8kZ0/f75tgjWTqef40Ucfsb6+vqxMJjPc5HK5jaM2nannqVKp2MmTJ7O33XYbe/z4cbakpIQ9cuQIm5OTY+PIjWfqOdbU1HT7d8zNzWX5fD770Ucf2TZwE5h6jp9++ikrEAjYTz/9lC0pKWH379/PSqVS9qmnnjI7BkqWODRlyhT2scceM/xZo9GwYWFh7KZNm3o9/q677mJvv/32bvclJiay//d//2fVOAfD1HPsaujQoQ6TLA3mPFmWZdvb21mRSMTu3r3bWiEO2mDPkWVZNiEhgX3uueesEZ7FmHOe7e3t7NSpU9kPPviAXbp0qd0nS6ae40cffcSKxWIbRWc5pp7n9u3b2WHDhrFqtdpWIQ7aYN+Xb731FisSidjGxkZrhThopp7jY489xt5yyy3d7lu9ejV7ww03mB0DTcNxRK1W49SpU0hOTjbcx+PxkJycjIyMjF6fk5GR0e14AJgzZ06fx3PNnHN0RJY4z+bmZrS1tSEgIMBaYQ7KYM+RZVmkp6ejsLAQN910kzVDHRRzz3PDhg0ICQnBgw8+aIswB8Xcc2xsbMTQoUMRGRmJ+fPnIy8vzxbhms2c8/zf//6HpKQkPPbYYwgNDUVcXBxeeeUVaDQaW4VtEkt89nz44YdYtGgRvL29rRXmoJhzjlOnTsWpU6cMU3WXLl3CTz/9hNtuu83sOGgjXY5UV1dDo9EgNDS02/2hoaE4f/58r8+Ry+W9Hi+Xy60W52CYc46OyBLn+eyzzyIsLKxHMmwvzD1HhUKB8PBwqFQq8Pl8/Pvf/8asWbOsHa7ZzDnP48eP48MPP0ROTo4NIhw8c85x9OjR2LlzJ8aPHw+FQoEtW7Zg6tSpyMvLs+om4oNhznleunQJhw4dwr333ouffvoJRUVFePTRR9HW1obU1FRbhG2SwX72ZGVlITc3Fx9++KG1Qhw0c87xnnvuQXV1NaZNmwaWZdHe3o5HHnkE//jHP8yOg5IlQjj26quvYu/evThy5IjDFM0aSyQSIScnB42NjUhPT8fq1asxbNgwzJgxg+vQLKKhoQH33XcfduzYgaCgIK7DsZqkpCQkJSUZ/jx16lSMGTMG//nPf7Bx40YOI7MsrVaLkJAQvP/+++Dz+Zg0aRLKy8vx+uuv22WyNFgffvghxo0bhylTpnAdikUdOXIEr7zyCv79738jMTERRUVFePLJJ7Fx40Y8//zzZr0mJUscCQoKAp/PR2VlZbf7KysrIZFIen2ORCIx6XiumXOOjmgw57llyxa8+uqr+OWXXzB+/Hhrhjko5p4jj8fDiBEjAADx8fEoKCjApk2b7DZZMvU8i4uLcfnyZcydO9dwn1arBQC4ubmhsLAQw4cPt27QJrLE+9Ld3R0JCQkoKiqyRogWYc55SqVSuLu7g8/nG+4bM2YM5HI51Go1PDw8rBqzqQbzb9nU1IS9e/diw4YN1gxx0Mw5x+effx733XcfHnroIQDAuHHj0NTUhIcffhj//Oc/weOZXoFENUsc8fDwwKRJk5Cenm64T6vVIj09vdsVXFdJSUndjgeAgwcP9nk818w5R0dk7nlu3rwZGzduRFpaGiZPnmyLUM1mqX9LrVYLlUpljRAtwtTzjImJwblz55CTk2O4zZs3DzfffDNycnIQGRlpy/CNYol/S41Gg3PnzkEqlVorzEEz5zxvuOEGFBUVGRJeALhw4QKkUqndJUrA4P4tv/rqK6hUKixZssTaYQ6KOefY3NzcIyHSJ8Csudvhml0aTgZt7969rEAgYHft2sXm5+ezDz/8MOvn52dYknvfffexa9euNRx/4sQJ1s3Njd2yZQtbUFDApqamOkTrAFPOUaVSsadPn2ZPnz7NSqVS9umnn2ZPnz7NXrx4katTMIqp5/nqq6+yHh4e7Ndff91tGW9DQwNXpzAgU8/xlVdeYQ8cOMAWFxez+fn57JYtW1g3Nzd2x44dXJ2CUUw9zz9zhNVwpp7j+vXr2f3797PFxcXsqVOn2EWLFrFCoZDNy8vj6hSMYup5lpaWsiKRiF25ciVbWFjI/vDDD2xISAj70ksvcXUKAzL393XatGns3XffbetwzWLqOaamprIikYj9/PPP2UuXLrEHDhxghw8fzt51111mx0DJEsfeffdddsiQIayHhwc7ZcoU9uTJk4bHpk+fzi5durTb8V9++SU7atQo1sPDgx07diz7448/2jhi05lyjiUlJSyAHrfp06fbPnATmXKeQ4cO7fU8U1NTbR+4CUw5x3/+85/siBEjWKFQyPr7+7NJSUns3r17OYjadKa+L7tyhGSJZU07x1WrVhmODQ0NZW+77TY2Ozubg6hNZ+q/5W+//cYmJiayAoGAHTZsGPvyyy+z7e3tNo7aNKae4/nz51kA7IEDB2wcqflMOce2tjb2xRdfZIcPH84KhUI2MjKSffTRR9m6ujqzfz7DsuaOSRFCCCGEOD+qWSKEEEII6QclS4QQQggh/aBkiRBCCCGkH5QsEUIIIYT0g5IlQgghhJB+ULJECCGEENIPSpYIIYQQQvpByRIhhBBCSD8oWSKEEDt0+fJlMAyDnJwcm//sqKgoMAwDhmFQX18/4PH6WBmGQXx8vNXjI8TWKFkihBArefHFF41KHpYtW4YFCxZ0uy8yMhIymQxxcXHWCW4AGzZsgEwmg1gsHvBYfaxr1qyxQWSE2J4b1wEQQog5WJaFRqOBm5tzfozx+XxIJBLOfr5IJDL65+tj9fHxsXJUhHCDRpYIcTEzZszA448/jlWrVsHf3x+hoaHYsWMHmpqa8MADD0AkEmHEiBH4+eefuz0vNzcXt956K3x8fBAaGor77rsP1dXVhsfT0tIwbdo0+Pn5ITAwEH/5y19QXFxseFytVmPlypWQSqUQCoUYOnQoNm3aBKD3Kaf6+nowDIMjR44AAI4cOQKGYfDzzz9j0qRJEAgEOH78OLRaLTZt2oTo6Gh4enpiwoQJ+Prrrw2vo3/e/v37kZCQAE9PT9xyyy2oqqrCzz//jDFjxsDX1xf33HMPmpubDc8z9nXT09MxefJkeHl5YerUqSgsLAQA7Nq1C+vXr8eZM2cMU1S7du3q8e/x4osvYvfu3fj+++8Nxx05cqTH34m1zsNYV65cwdy5c+Hv7w9vb2+MHTsWP/30k8mvQ4hDMnsLXkKIQ5o+fTorEonYjRs3shcuXGA3btzI8vl89tZbb2Xff/999sKFC+yKFSvYwMBAtqmpiWVZlq2rq2ODg4PZdevWsQUFBWx2djY7a9Ys9uabbza87tdff81+88037MWLF9nTp0+zc+fOZceNG8dqNBqWZVn29ddfZyMjI9ljx46xly9fZn/99Vf2s88+Y1mWZUtKSlgA7OnTpw2vV1dXxwJgDx8+zLIsyx4+fJgFwI4fP549cOAAW1RUxNbU1LAvvfQSGxMTw6alpbHFxcXsRx99xAoEAvbIkSPdnnf99dezx48fZ7Ozs9kRI0aw06dPZ2fPns1mZ2ezx44dYwMDA9lXX33V8PONfd3ExET2yJEjbF5eHnvjjTeyU6dOZVmWZZubm9k1a9awY8eOZWUyGSuTydjm5uYe/x4NDQ3sXXfdxaakpBiOU6lUPf5OrHUevRk6dCj71ltvdbvv9ttvZ2fNmsWePXuWLS4uZvft28cePXq02zGpqanshAkT+nxdQhwVJUuEuJjp06ez06ZNM/y5vb2d9fb2Zu+77z7DfTKZjAXAZmRksCzLshs3bmRnz57d7XXKyspYAGxhYWGvP+fatWssAPbcuXMsy7Ls448/zt5yyy2sVqvtcawpydJ3331nOKa1tZX18vJif/vtt26v9+CDD7KLFy/u9rxffvnF8PimTZtYAGxxcbHhvv/7v/9j58yZM6jX/fHHH1kAbEtLC8uyxicPS5cuZefPn9/v34m1zqM3vSVL48aNY1988cV+z4OSJeKsnHOynxDSr/Hjxxv+n8/nIzAwEOPGjTPcFxoaCgCoqqoCAJw5cwaHDx/utSaluLgYo0aNwsWLF/HCCy8gMzMT1dXV0Gq1AIDS0lLExcVh2bJlmDVrFkaPHo2UlBT85S9/wezZs02OffLkyYb/LyoqQnNzM2bNmtXtGLVajYSEhD7POTQ0FF5eXhg2bFi3+7Kysgb1ulKpFIDu723IkCEmn5sxrHUeA3niiSewYsUKHDhwAMnJyVi4cGG3WAhxZpQsEeKC3N3du/2ZYZhu9zEMAwCGhKexsRFz587Fa6+91uO19AnC3LlzMXToUOzYsQNhYWHQarWIi4uDWq0GAEycOBElJSX4+eef8csvv+Cuu+5CcnIyvv76a/B4uvJJlmUNr9vW1tZr7N7e3ob/b2xsBAD8+OOPCA8P73acQCDo85z/fL76+7qer7mvC3T+vVmDtc5jIA899BDmzJmDH3/8EQcOHMCmTZvwxhtv4PHHHzfnNAhxKJQsEUIGNHHiRHzzzTeIiorqdfVZTU0NCgsLsWPHDtx4440AgOPHj/c4ztfXF3fffTfuvvtu/O1vf0NKSgpqa2sRHBwMAJDJZIYRD2P6C8XGxkIgEKC0tBTTp08fxBla53U9PDyg0WgsdpypLP33ExkZiUceeQSPPPII1q1bhx07dlCyRFwCJUuEkAE99thj2LFjBxYvXoxnnnkGAQEBKCoqwt69e/HBBx/A398fgYGBeP/99yGVSlFaWoq1a9d2e40333wTUqkUCQkJ4PF4+OqrryCRSODn5wcej4frr78er776KqKjo1FVVYXnnntuwLhEIhGefvppPPXUU9BqtZg2bRoUCgVOnDgBX19fLF261KzztdTrRkVFoaSkBDk5OYiIiIBIJOp1RCcqKgr79+9HYWEhAgMDjeptZMvzAIBVq1bh1ltvxahRo1BXV4fDhw9jzJgxFomTEHtHyRIhZEBhYWE4ceIEnn32WcyePRsqlQpDhw5FSkoKeDweGIbB3r178cQTTyAuLg6jR4/GO++8gxkzZhheQyQSYfPmzbh48SL4fD6uu+46/PTTT4YpuJ07d+LBBx/EpEmTMHr0aGzevNmomqaNGzciODgYmzZtwqVLl+Dn54eJEyfiH//4x6DO2RKvu3DhQnz77be4+eabUV9fj48++gjLli3rcdzy5ctx5MgRTJ48GY2NjTh8+DCioqIGFb8lzwMANBoNHnvsMVy9ehW+vr5ISUnBW2+9ZZEYCbF3DNu1SIAQQojLi4qKwqpVq7Bq1SqTnvfiiy/iu+++42SLFkKsiZIlQggh3URFRUEmk8Hd3R3l5eUDTguWlpYiNjYWarUasbGxlCwRp0PJEiGEkG6uXLliWI04bNgww1RpX9rb23H58mUAulV2kZGR1g6REJuiZIkQQgghpB+0NxwhhBBCSD8oWSKEEEII6QclS4QQQggh/aBkiRBCCCGkH5QsEUIIIYT0g5IlQgghhJB+ULJECCGEENIPSpYIIYQQQvrx/wEx2G3qqzThIAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "with h5tbx.File(vel_hdf_filename) as h5:\n", " vel_data = h5['vel'][:]\n", " vel_data.plot(marker='o')\n", " \n", "vel_data # this returns the interactive view of the array and its meta data" ] }, { "cell_type": "markdown", "id": "1627e47a-37d1-45b2-8adc-d04d038b1574", "metadata": {}, "source": [ "### Natural Naming\n", "Until here, we used the conventional way of addressing variables and groups in a dictionary-like style. `h5RDMtoolbox` allows using \"natural naming\" which means that we can address those objects as if they were attributes. Make sure `h5tbx.config.natural_naming` is set to `True` (the default)" ] }, { "cell_type": "markdown", "id": "d1b678a5-403f-4055-9d1b-45153dcb7b58", "metadata": {}, "source": [ "Let's first disable `natural_naming`:" ] }, { "cell_type": "code", "execution_count": 7, "id": "224c1b74-bd60-4c2e-b498-1f3dab56aa7b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'File' object has no attribute 'vel'\n" ] } ], "source": [ "with h5tbx.set_config(natural_naming=False):\n", " with h5tbx.File(vel_hdf_filename, 'r') as h5:\n", " try:\n", " ds = h5.vel[:]\n", " except Exception as e:\n", " print(e)" ] }, { "cell_type": "markdown", "id": "3514ae51-07ca-4e97-a37f-bbd3a8c29f3d", "metadata": {}, "source": [ "Enable it:" ] }, { "cell_type": "code", "execution_count": 8, "id": "0a088543-0338-4022-a691-4108a95b1951", "metadata": { "tags": [] }, "outputs": [], "source": [ "with h5tbx.set_config(natural_naming=True):\n", " with h5tbx.File(vel_hdf_filename, 'r') as h5:\n", " ds = h5.vel[:]" ] }, { "cell_type": "markdown", "id": "fdcc735d-8422-4710-a794-984a59580ad6", "metadata": {}, "source": [ "## Inspect file content\n", "Often it is necessary to inspect the content of a file (structure, metadata, not the raw data). Calling `dump()` on a group represents the content (dataset, groups and attributes) as a pretty and interactive (!) HTML representation. This is adopted from the `xarray` package. All credits for this idea go there. The representation here avoids showing data, though. Outside an IPython environment, call `sdump()` to get a string representation of the file." ] }, { "cell_type": "code", "execution_count": 9, "id": "db6775f0-f3f0-40ac-887d-d00068188138", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "\n", "
    \n", "
  • \n", " \n", " \n", " \n", "
      \n", "
    \n", "\n", "
      \n", " \n", " \n", " (21) [float64]\n", "
      • long_name: measurement time
      • units: s
      • \n", "
      \n", "
    \n", "\n", "
      \n", " \n", " \n", " (time: 21) [float64]\n", "
      • long_name: air velocity in pipe
      • units: m/s
      • \n", "
      \n", "
    \n", "
  • \n", "
\n", "
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "with h5tbx.File(vel_hdf_filename) as h5:\n", " h5.dump()" ] }, { "cell_type": "code", "execution_count": 10, "id": "71f5ebb1-2587-41f1-a9eb-1f52b9ba70de", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1mtime\u001b[0m: (21,), dtype: float64\n", " \u001b[3ma: long_name\u001b[0m: measurement time\n", " \u001b[3ma: units\u001b[0m: s\n", "\u001b[1mvel\u001b[0m: (21,), dtype: float64\n", " \u001b[3ma: long_name\u001b[0m: air velocity in pipe\n", " \u001b[3ma: units\u001b[0m: m/s\n" ] } ], "source": [ "with h5tbx.File(vel_hdf_filename) as h5:\n", " h5.sdump()" ] }, { "cell_type": "markdown", "id": "26114e99-7d0e-4b6f-8b41-7c6e8e76b78b", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "## Conventions\n", "The file content is controlled by means of a `convention`. This means that specific attributes are required for HDF groups or datasets.\n", "\n", "They can be understood as rules, which are validated during usage. To make those rules to become effective, the convention must be imported and enabled. Conventions can be created by the user, too. More on this [here](../userguide/convention/index.rst).\n", "\n", "For now, we select the existing one, which is published on [Zenodo](https://zenodo.org/record/10428795)" ] }, { "cell_type": "code", "execution_count": 11, "id": "d57bb0fe-9187-4350-9c1e-1fe117613dd2", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "Convention(\"h5rdmtoolbox-tutorial-convention\")" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cv = h5tbx.convention.from_zenodo('10428822')\n", "cv" ] }, { "cell_type": "markdown", "id": "a073571b-0284-487f-9758-4bff9e9891d6", "metadata": {}, "source": [ "From the above representation string of the convention object we can read which attributes are *optional* or **required** for file creation (`__init__`), dataset creation (`create_dataset`) or group creation (`create_group`)." ] }, { "cell_type": "markdown", "id": "22c3c0f6-4a1d-452a-85c6-1731a42b0bf4", "metadata": {}, "source": [ "Without enabling the convention, the working with HDF5 files through the `h5rdmtoolbox` is almost (we got a few additional features which make life a bit easier) as by using `h5py`:" ] }, { "cell_type": "code", "execution_count": 12, "id": "fbfd20b3-62c7-4c00-bcec-76b937c9fad5", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "\n", "
    \n", "
  • \n", " \n", " \n", " \n", "
      \n", "
    \n", "
  • \n", "
\n", "
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "with h5tbx.File() as h5:\n", " h5.dump()" ] }, { "cell_type": "markdown", "id": "efa06687-c9c0-4a20-af30-91f337fb09d5", "metadata": {}, "source": [ "**Now, we enable the convention ...**" ] }, { "cell_type": "code", "execution_count": 13, "id": "a1bb2a60-752c-4d14-844e-16841a060d0a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "using(\"h5rdmtoolbox-tutorial-convention\")" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "h5tbx.use(cv)" ] }, { "cell_type": "markdown", "id": "d071903e-e60c-45c6-a4e5-758533975171", "metadata": {}, "source": [ "... and get an error, because we are not providing a \"data_type\":" ] }, { "cell_type": "code", "execution_count": 14, "id": "e5049224-a978-4baf-ada6-70b8ede3dde3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Convention \"h5rdmtoolbox-tutorial-convention\" expects standard attribute \"data_type\" to be provided as an argument during file creation.\n" ] } ], "source": [ "try:\n", " with h5tbx.File() as h5:\n", " pass\n", "except Exception as e:\n", " print(e)" ] }, { "cell_type": "code", "execution_count": 15, "id": "cdcea207-3156-48bb-b13e-719bc1849128", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "time = np.linspace(0, np.pi/4, 21) # units [s]\n", "signal = np.sin(2*np.pi*3*time) # units [V], physical: [m/s]\n", "\n", "with h5tbx.File(contact=h5tbx.__author_orcid__, data_type='experimental') as h5:\n", " vel_hdf_filename = h5.hdf_filename # store for later use\n", " \n", " ds_time = h5.create_dataset(name='time',\n", " data=time, \n", " units='s',\n", " long_name='measurement time',\n", " make_scale=True)\n", " \n", " ds_signal = h5.create_dataset(name='vel',\n", " data=signal,\n", " units='m/s',\n", " long_name='air velocity in pipe',\n", " attach_scale=ds_time)" ] }, { "cell_type": "markdown", "id": "286e0c62-957c-46ad-95ed-a75800d5a47f", "metadata": {}, "source": [ "## RDF\n", "\n", "The files can be described by RDF triples:" ] }, { "cell_type": "code", "execution_count": 16, "id": "c4421b57-b1f4-40c1-b86b-c8aa1ce4369c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "\n", " \n", "
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "h5tbx.use(None)\n", "with h5tbx.File() as h5:\n", " grp = h5.create_group('contact', attrs=dict(orcid='https://orcid.org/0000-0001-8729-0482')) \n", " grp.rdf.predicate = 'https://schema.org/author'\n", " grp.rdf.type = 'http://xmlns.com/foaf/0.1/Person' # what the content of group is, namely a foaf:Person\n", " grp.rdf.subject = 'https://orcid.org/0000-0001-8729-0482' # corresponds to @ID in JSON-LD\n", " grp.rdf.predicate['orcid'] = 'http://w3id.org/nfdi4ing/metadata4ing#orcidId'\n", " grp.attrs['first_name', 'http://xmlns.com/foaf/0.1/firstName'] = 'Matthias'\n", "\n", " h5.dump()" ] }, { "cell_type": "markdown", "id": "a833ff03-c6c9-46ac-a993-6c008e06a219", "metadata": {}, "source": [ "One of the benefits is that the user can understand the meaning of the data. A machine-interpretable and standardised common exchange file format used by Semantic Web technology is JSON-LD. The toolbox also allows exporting to this format:" ] }, { "cell_type": "code", "execution_count": 17, "id": "ffab3f88-c075-42ac-80d2-471fc5cb49ad", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"@context\": {\n", " \"owl\": \"http://www.w3.org/2002/07/owl#\",\n", " \"rdfs\": \"http://www.w3.org/2000/01/rdf-schema#\",\n", " \"hdf5\": \"http://purl.allotrope.org/ontologies/hdf5/1.8#\"\n", " },\n", " \"@type\": \"hdf5:File\",\n", " \"hdf5:rootGroup\": {\n", " \"@type\": \"hdf5:Group\",\n", " \"hdf5:attribute\": [],\n", " \"hdf5:name\": \"/\",\n", " \"hdf5:member\": [\n", " {\n", " \"@type\": \"hdf5:Group\",\n", " \"hdf5:attribute\": [\n", " {\n", " \"@type\": \"hdf5:Attribute\",\n", " \"hdf5:name\": \"first_name\",\n", " \"hdf5:value\": \"Matthias\",\n", " \"@id\": \"_:N8b15f3723ef74328b473c25d5a91b0a1\"\n", " },\n", " {\n", " \"@type\": \"hdf5:Attribute\",\n", " \"hdf5:name\": \"orcid\",\n", " \"hdf5:value\": \"https://orcid.org/0000-0001-8729-0482\",\n", " \"@id\": \"_:Ndcbf5fa7e56e47dba6ed50274b0714ad\"\n", " }\n", " ],\n", " \"hdf5:name\": \"/contact\",\n", " \"@id\": \"_:Nbef7c3f8aeac497e9e8348bbf5638aaf\"\n", " }\n", " ],\n", " \"@id\": \"_:Nadc75cefde5c4e27abc77f7cbba7ed30\"\n", " },\n", " \"@id\": \"_:Ne482057db09d415fabf64de70265a75f\"\n", "}\n" ] } ], "source": [ "print(h5tbx.jsonld.dump_file(h5.hdf_filename, skipND=1))" ] }, { "cell_type": "markdown", "id": "026565ee-10c0-4599-89dc-f199dd50b4f9", "metadata": {}, "source": [ "## Databases\n", "\n", "The `h5rdmtoolbox` has currently implemented two solutions to use databases with HDF5 file. One solution is mapping metadata into a [mongoDB](https://www.mongodb.com/) database. The other uses the HDF5 file itself as a database and allows querying without any further step.\n", "\n", "In this quick tutorial, we use the second solution. More on the topic can be found in the [documentation](https://h5rdmtoolbox.readthedocs.io/en/latest/database/index.html)\n", "\n", "Let's find the dataset with name \"/vel\" (yes, trivial in this case, but just to get an idea). We use `find_one`, because we want to find only one (the first) occurrence:" ] }, { "cell_type": "code", "execution_count": 18, "id": "a3158ff0-0edf-416b-b07a-c6269c91b266", "metadata": {}, "outputs": [], "source": [ "from h5rdmtoolbox.database import FileDB" ] }, { "cell_type": "code", "execution_count": 19, "id": "82c6af1b-a13b-49ee-996c-825a2ced8883", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/vel\n" ] } ], "source": [ "res = FileDB(vel_hdf_filename).find_one({'$name': '/vel'})\n", "print(res.name)" ] }, { "cell_type": "markdown", "id": "abdfd9f0-169e-4300-bf68-2a169e270cff", "metadata": {}, "source": [ "The same can be done from an opened file, too:" ] }, { "cell_type": "code", "execution_count": 20, "id": "28d13a5e-7267-4787-971e-eddca4f47ca1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'/vel'" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "with h5tbx.File(vel_hdf_filename) as h5:\n", " res = h5.find_one({'$name': '/vel'})\n", "res.name" ] }, { "cell_type": "markdown", "id": "300a9eb5-85fb-48e0-adfc-9141a0f0e0ab", "metadata": {}, "source": [ "Let's find all (`find`) datasets with the attribute \"units\" and any value:" ] }, { "cell_type": "code", "execution_count": 21, "id": "19f818a6-b087-4880-9c42-e65a6bfd943f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n" ] } ], "source": [ "res = FileDB(vel_hdf_filename).find({'units': {'$regex': '.*'}})\n", "for r in res:\n", " print(r)" ] }, { "cell_type": "markdown", "id": "54b88c46-3640-46fa-86f1-4a2b92fef688", "metadata": {}, "source": [ "## Layouts\n", "\n", "Layouts define how a file is expected to be organized, which groups and datasets must exist, which attributes are expected and much more. Layout define expectations and thus help file exchange where multiple users are involved. In the jargon of the toolbox, we call these \"specifications\".\n", "\n", "**Design concept**
\n", "The module *layouts* makes use of the database solution for HDF5 files. The idea is, that we should be able to formulate our expectations/specifications in the form of a query. For more detailed information, see [here](https://h5rdmtoolbox.readthedocs.io/en/latest/layouts.html). So we write down our queries, which we expect to find HDF5 objects in a file, when we validate one in the future.\n", "\n", "Let's design a simple one, which requires all datasets to have the attribute \"units\":" ] }, { "cell_type": "code", "execution_count": 22, "id": "e4aae76a-0e2a-4adc-9522-6707cc080bed", "metadata": {}, "outputs": [], "source": [ "from h5rdmtoolbox.layout import Layout" ] }, { "cell_type": "code", "execution_count": 23, "id": "b9284d79-3462-45ef-b445-bdedf5a12387", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[LayoutSpecification(kwargs={'flt': {}, 'objfilter': 'dataset'})]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lay = Layout()\n", "\n", "spec_all_dataset = lay.add(\n", " FileDB.find, # query function\n", " flt={},\n", " objfilter='dataset',\n", " n=None\n", ")\n", "\n", "# The following specification is added to the previous.\n", "# This will apply the query only on results found by the previous query\n", "spec_compression = spec_all_dataset.add(\n", " FileDB.find,\n", " flt={'units': {'$exists': True}}, # attribute \"units\" exists\n", " n=1\n", ")\n", "\n", "# we added one specification to the layout. let's check:\n", "lay.specifications # note, that the second specification is not shown, because it is part of the first one" ] }, { "cell_type": "code", "execution_count": 24, "id": "f3408c38-a67a-476f-818c-5ac139eeb9a6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "res = lay.validate(vel_hdf_filename)\n", "res.is_valid()" ] }, { "cell_type": "code", "execution_count": 25, "id": "fe9731be-02c6-4a84-bfee-cdc3c9257ab6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Summary of layout validation\n", "+--------------------------------------+----------+--------+----------------------+---------------+-----------------------------------------+\n", "| id | called | flag | flag description | description | func |\n", "|--------------------------------------+----------+--------+----------------------+---------------+-----------------------------------------|\n", "| 1b900c80-2e76-45c7-8fdf-ac44fe95fa6b | True | 17 | SUCCESSFUL, OPTIONAL | | h5rdmtoolbox.database.hdfdb.filedb.find |\n", "| 3229534f-a3a2-44aa-8d45-13af20b9da8f | True | 1 | SUCCESSFUL | | h5rdmtoolbox.database.hdfdb.filedb.find |\n", "| 3229534f-a3a2-44aa-8d45-13af20b9da8f | True | 1 | SUCCESSFUL | | h5rdmtoolbox.database.hdfdb.filedb.find |\n", "+--------------------------------------+----------+--------+----------------------+---------------+-----------------------------------------+\n", "--> Layout is valid\n" ] } ], "source": [ "res.print_summary(exclude_keys=('kwargs', 'target_name', 'target_type'))" ] }, { "cell_type": "markdown", "id": "4724c12c-4b0b-40c3-9986-7f53c8bc2d37", "metadata": {}, "source": [ "The above layout successfully validate the file.\n", "\n", "Now, let's add the specification:\n", "- The file must have one dataset named \"pressure\".\n", "- The exact location within the file does not play a role.\n", "- This specific dataset must have the unit \"Pa\":\n", "- The shape of the dataset must be equal to (21, )" ] }, { "cell_type": "code", "execution_count": 26, "id": "298c4bea-4396-48a1-8514-40c761c4d099", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[LayoutSpecification(kwargs={'flt': {}, 'objfilter': 'dataset'}),\n", " LayoutSpecification(kwargs={'flt': {'$name': {'$regex': 'pressure'}, '$shape': (21,), 'units': 'Pa'}, 'objfilter': 'dataset'})]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lay.add(\n", " FileDB.find_one, # query function\n", " flt={'$name': {'$regex': 'pressure'}, \n", " '$shape': (21, ),\n", " 'units': 'Pa'},\n", " objfilter='dataset',\n", " n=1\n", ")\n", "lay.specifications" ] }, { "cell_type": "markdown", "id": "06d9a40a-90bf-469f-97b3-7e9fa0f5aa04", "metadata": {}, "source": [ "The validation now fails:" ] }, { "cell_type": "code", "execution_count": 27, "id": "7bfb6954-4b82-46a4-ba23-65f716524861", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2024-07-28_10:36:50,572 ERROR [core.py:330] Applying spec. \"LayoutSpecification(kwargs={'flt': {'$name': {'$regex': 'pressure'}, '$shape': (21,), 'units': 'Pa'}, 'objfilter': 'dataset'})\" failed due to not matching the number of results: 1 != 0\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "res = lay.validate(vel_hdf_filename)\n", "res.is_valid()" ] }, { "cell_type": "markdown", "id": "c7764cb0-cb3b-41cc-b681-ab2fd0d894ea", "metadata": {}, "source": [ "Let's add such a dataset:" ] }, { "cell_type": "code", "execution_count": 28, "id": "d537c483-9c2d-4361-9ed8-371df1574314", "metadata": {}, "outputs": [], "source": [ "with h5tbx.File(vel_hdf_filename, 'r+') as h5:\n", " h5.create_dataset('subgrp/pressure', shape=(21,), attrs={'units': 'Pa'})" ] }, { "cell_type": "markdown", "id": "b271037d-d0d4-482b-8ce0-beb6e7f0f0e6", "metadata": {}, "source": [ "And perform the validation again:" ] }, { "cell_type": "code", "execution_count": 29, "id": "8572d50e-9f4d-4c38-aa4e-d47a9b682c87", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "res = lay.validate(vel_hdf_filename)\n", "res.is_valid()" ] }, { "cell_type": "markdown", "id": "b575c1a3-f194-4959-9ebc-1fc960056e6c", "metadata": {}, "source": [ "Feel free to play with the layout specifications and the HDF5 file content. For sure, knowledge about performing queries with the used database is needed." ] }, { "cell_type": "markdown", "id": "894b7854-339f-4fa0-b1f2-03e74f77d07e", "metadata": {}, "source": [ "## Repositories\n", "\n", "Finally, we can publish our data. The toolbox has implemented an interface to [Zenodo](https://zenodo.org/). Using it with the sandbox (testing) environment requires an API TOKEN. For this, please provide the environment variable \"ZENODO_SANDBOX_API_TOKEN\":" ] }, { "cell_type": "code", "execution_count": 30, "id": "136c1e6f-9de1-4abf-a86e-cd61486242c0", "metadata": {}, "outputs": [], "source": [ "# %set_env ZENODO_SANDBOX_API_TOKEN=" ] }, { "cell_type": "code", "execution_count": 31, "id": "ed5bb708-ee10-430b-aa25-a4f2a971df16", "metadata": {}, "outputs": [], "source": [ "from h5rdmtoolbox.repository import zenodo\n", "from datetime import datetime" ] }, { "cell_type": "markdown", "id": "efc27e17-25ab-4566-b247-e00964a8cd7d", "metadata": {}, "source": [ "Create a new deposit (repo in the testing environment):" ] }, { "cell_type": "code", "execution_count": 32, "id": "2706416a-e19b-46ec-b0cd-6a9c04b2eb5a", "metadata": {}, "outputs": [], "source": [ "deposit = zenodo.ZenodoSandboxDeposit(None)" ] }, { "cell_type": "markdown", "id": "e1deefa4-b487-49ae-a61e-440e37507abb", "metadata": {}, "source": [ "Prepare metadata according to the Zenodo API: " ] }, { "cell_type": "code", "execution_count": 33, "id": "620ef32e-e68b-4dc8-935a-bc002dfbdbb4", "metadata": {}, "outputs": [], "source": [ "meta = zenodo.metadata.Metadata(\n", " version=\"1.0.0\",\n", " title='H5TBX Quick Overview Test',\n", " description=f'The file created in the quick overview script using the h5rdmtoolbox version {h5tbx.__version__}.',\n", " creators=[zenodo.metadata.Creator(name=\"Probst, Matthias\",\n", " affiliation=\"Karlsruhe Institute of Technology, Institute for Thermal Turbomachinery\",\n", " orcid=\"0000-0001-8729-0482\")],\n", " upload_type='dataset',\n", " access_right='open',\n", " keywords=['h5rdmtoolbox', 'tutorial', 'repository-test'],\n", " publication_date=datetime.now(),\n", ")" ] }, { "cell_type": "markdown", "id": "a70f2ed2-96d4-4a26-a0e2-809dec839e8b", "metadata": {}, "source": [ "push metadata to the repository:" ] }, { "cell_type": "code", "execution_count": 34, "id": "0c0dbdf4-13f9-4364-beda-0184ff9de99d", "metadata": {}, "outputs": [], "source": [ "deposit.metadata = meta" ] }, { "cell_type": "markdown", "id": "412c4fe0-d1b1-4baa-bd3a-395a46f502b0", "metadata": {}, "source": [ "**Upload the HDF5 file:**
\n", "As HDF5 files cannot be previewed in the repository web interface and HDF5 files may become very large, it is handy to upload a metadata file as an additional resource. This allows to preview the metadata content before downloading the larger HDF5 file.\n", "\n", "This functionality is given by the upload method `.upload_file`, which takes the optional parameter `metamapper`. Please provide a function which creates a file with metadata. The toolbox comes with a solution, that maps the HDF5 metadata content into a JSON-LD file:" ] }, { "cell_type": "code", "execution_count": 35, "id": "24029142-a2ff-4417-8aca-6bc614355abc", "metadata": {}, "outputs": [], "source": [ "from h5rdmtoolbox import jsonld" ] }, { "cell_type": "code", "execution_count": 36, "id": "ba875bb7-40da-4938-bb6c-74aeebce2fa6", "metadata": {}, "outputs": [], "source": [ "deposit.upload_file(filename=vel_hdf_filename, metamapper=jsonld.hdf2jsonld, skipND=1) # skipND is needed by hdf2jsonld" ] } ], "metadata": { "celltoolbar": "Slideshow", "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.19" } }, "nbformat": 4, "nbformat_minor": 5 }